XAML 在MAUI集合视图中是否有一个引用当前项目的变量?

bnl4lu3b  于 2023-08-01  发布在  其他
关注(0)|答案(2)|浏览(111)

我正在努力解决如何引用/绑定到CollectionViewDataTemplate中的当前项。
我已经看过像this这样的问题,并且 * 认为 * 我可以将解决方案应用于MAUI,因为它和WPF都使用XAML,但Intellisense抱怨{Binding /}不是有效的绑定路径。执行more digging后发现我应该使用{Binding .}。所以我尝试了,但是,试图运行我的应用程序的结果是它甚至没有加载在所有。
我的xaml现在看起来像这样:
TreeNode.xaml

<maui:ReactiveContentView
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:maui="clr-namespace:ReactiveUI.Maui;assembly=ReactiveUI.Maui"
    xmlns:vm="clr-namespace:turquoise_frontend.Controls"
    xmlns:math="clr-namespace:HexInnovation;assembly=MathConverter.Maui"
    x:TypeArguments="vm:TreeNodeModel"
    x:DataType="vm:TreeNodeModel"
    x:Class="turquoise_frontend.Controls.TreeNode" x:Name="this">
    <VerticalStackLayout x:Name="sl2">
        <!-- stuff omitted due to being irrelevant here... -->
        <CollectionView ItemsSource="Children" x:Name="_ChildrenStackLayout" CanMixGroups="True" CanReorderItems="True" ReorderCompleted="_ChildrenStackLayout_ReorderCompleted">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <vm:TreeNode/>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</maui:ReactiveContentView>

字符串
我猜测,运行时将{Binding .}解释为当前xaml文件所表示的对象,而不是ItemTemplate所表示的对象,从而导致了无限递归。
那么,如果我想创建一个可以利用CollectionView并且不会无限循环的递归布局,我应该写什么呢?我现在想做的事有可能吗
TreeNode.xaml.cs

public partial class TreeNode : ReactiveContentView<TreeNodeModel>
    {
        public double? IndentWidth => Depth * SpacerWidth;
        public double? PaddingWidth => IndentWidth + 30;
        public int SpacerWidth { get; } = 40;
        private int? Depth => ViewModel?.Value.Depth ?? 0;
        public string ExpandText { get => (ViewModel?.Children?.Count ?? 0) > 0 ? "▷" : ""; }

        public TreeNode(TreeNodeModel tnm)
        {
            this.WhenAnyValue(x => x.ViewModel.isExpanded).ObserveOn(RxApp.MainThreadScheduler).Subscribe(b =>
            {
                _ChildrenStackLayout.IsVisible = b;
            });

            this.WhenAnyValue(b => b.ViewModel.selected).ObserveOn(RxApp.MainThreadScheduler).Subscribe(a =>
            {
                SelectionBoxView.IsVisible = a;
            });
            this.WhenAnyValue(b => b.ViewModel).ObserveOn(RxApp.MainThreadScheduler).WhereNotNull().Subscribe(vm =>
            {
                vm.Children.CollectionChanged += (o, e) =>
                            {
                                _SpacerBoxView.WidthRequest = IndentWidth ?? 0;
                            };
            });

            
            BindingContext = ViewModel = tnm;

            InitializeComponent();
        }
}


TreeNodeModel.cs

public class TreeNodeModel : ReactiveObject
    {
        private ITreeNode _value;

        private ObservableCollection<TreeNodeModel> _children;
        private TreeNodeModel? _parent;

        private bool _toggled;
        private bool _selected;
        private bool _expanded;

        private string contract;

        [Reactive]
        public ITreeNode Value { get => _value; set => this.RaiseAndSetIfChanged(ref _value, value); }

        [Reactive]
        public TreeNodeModel? Parent
        {
            get => _parent; set
            {
                _parent = value;
                Value.Parent = value?.Value;
            }
        }

        [Reactive]
        public ObservableCollection<TreeNodeModel> Children
        {
            get
            {
                var nc = new ObservableCollection<TreeNodeModel>();
                foreach (var i in Value.Children ?? new ObservableCollection<ITreeNode>())
                {
                    var b = new TreeNodeModel(i, this.contract)
                    {
                        Tree = this.Tree
                    };
                    b.Parent = this;
                    nc.Add(b);
                }
                return nc;
            }
        }

        public TreeNodeModel Root
        {
            get
            {
                TreeNodeModel top = this;
                do
                {
                    if (top.Parent != null)
                        top = top.Parent;
                    else break;
                } while (top != null);
                return top ?? this;
            }
        }

        [Reactive]
        public bool isExpanded { get => _expanded; set => this.RaiseAndSetIfChanged(ref _expanded, value); }
        [Reactive] public bool Toggled { get => _toggled; set => this.RaiseAndSetIfChanged(ref _toggled, value); }

        public TreeModel Tree { get; set; }

        [Reactive]
        public bool selected { get => _selected; set => this.RaiseAndSetIfChanged(ref _selected, value); }

        public TreeNodeModel(ITreeNode val, string contract)
        {
            this.contract = contract;
            Toggled = val.Toggled;
            selected = false;
            isExpanded = false;
            
            _value = val;
            if (val.Parent != null)
                _parent = new TreeNodeModel(val.Parent, contract) { 
                    Tree = this.Tree
                };
            this.WhenAnyValue(b => b.Toggled).Subscribe(a =>
            {
                _value.Toggled = a;
            });
            this.WhenAnyValue(b => b.isExpanded);
            this.WhenAnyValue(b => b.Toggled);
            this.WhenAnyValue(b => b.selected);
        }
    }


ITreeNode接口

public interface ITreeNode
    {
        public object Value { get; set; }
        public ObservableCollection<ITreeNode> Children { get; }
        public ITreeNode? Parent { get; set; }
        public int Depth { get; }
        public int ID();
        public bool IsBranch { get; }
        public string AsString { get; }
        public bool Toggled { get; set; }
        public void Add(ITreeNode tn, int index);
        public void Add(ITreeNode tn);
        public void Remove(ITreeNode tn);
        public void Remove(int index);
        public bool Equals(ITreeNode other);
        public event NotifyCollectionChangedEventHandler NotifyCollectionChanged;
    }

au9on6nz

au9on6nz1#

让我们来看看这一部分:

var nc = new ObservableCollection<TreeNodeModel>();
            foreach (var i in Value.Children ?? new ObservableCollection<ITreeNode>())
            {
                var b = new TreeNodeModel(i, this.contract)
                {
                    Tree = this.Tree
                };
                b.Parent = this;
                nc.Add(b);
            }
            return nc;

字符串
注意“B”是如何调用构造函数的:

public TreeNodeModel(ITreeNode val, string contract)
        {
            this.contract = contract;
            Toggled = val.Toggled;
            selected = false;
            isExpanded = false;
            
            _value = val;
            if (val.Parent != null)
                _parent = new TreeNodeModel(val.Parent, contract) { 
                    Tree = this.Tree
                };
            this.WhenAnyValue(b => b.Toggled).Subscribe(a =>
            {
                _value.Toggled = a;
            });
            this.WhenAnyValue(b => b.isExpanded);
            this.WhenAnyValue(b => b.Toggled);
            this.WhenAnyValue(b => b.selected);
        }


所以,这里发生了什么:
“i”(这是ITreeNode,提醒您正确命名变量)被放入构造函数中。
我们正在构造“B”(这是TreeNodeModel,同样的提醒)
如果我有父母
构造函数被再次调用,并且父级被设置。在这一行:

if (val.Parent != null)
                _parent = new TreeNodeModel(val.Parent, contract) { 
                    Tree = this.Tree
                };


然而,在这个递归完成之后,我们从这一行开始:

var b = new TreeNodeModel(i, this.contract)


到这一行:

b.Parent = this;


我们意识到有一半的代码什么也没做。而新构建的孩子和他们的父母(以及对该事件的订阅)会发生什么并不是很清楚。
还有这个:

<VerticalStackLayout x:Name="sl2">
    <CollectionView...


是不行。

i86rm4rw

i86rm4rw2#

DataTemplate中的{Binding .}将表示ItemsSource的单击项。

相关问题