簡體   English   中英

將INotifyPropertyChanged與包含復選框的WPF樹視圖一起使用時,防止無限循環

[英]Preventing infinite loops when using INotifyPropertyChanged with a WPF treeview containing checkboxes

這是我的第一個問題,如果格式不正確,我深表歉意。

我是WPF和MVVM的新手,我遇到了一個似乎無法解決的問題。

我有一個樹視圖,它顯示一個MenuItem層次結構,其中每個MenuItem都有一個復選框,用於父級和子級節點。 當前的解決方案允許用戶單擊父節點,並根據需要選中/取消選中所有子項。

我現在需要實現相反的操作,如果用戶單擊子節點之一,則應該選擇父節點(如果尚未選擇)。

我目前遇到的問題是,以編程方式檢查父節點會觸發父節點的INotifiedPropertyChanged事件,從而重新檢查我的子節點。

如何防止這種情況發生?

這是我的MenuItem代碼:

public class MenuItem : INotifyPropertyChanged
    {
        string _name;
        List<MenuItem> _subItems = new List<MenuItem>();
        bool _isChecked;
        MenuItem _parent;

        public List<MenuItem> SubItems
        {
            get { return _subItems; }
            set
            {
                _subItems = value;
                RaisePropertyChanged("SubItems");
            }
        }

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }

        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;
                RaisePropertyChanged("IsChecked");
            }
        }

        public MenuItem Parent
        {
            get { return _parent; }
            set
            {
                _parent = value;
                RaisePropertyChanged("Parent");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            if (propertyName == "IsChecked")
            {
                if (Parent == null)
                {
                    foreach (MenuItem Child in _subItems)
                        Child.IsChecked = this.IsChecked;
                }

                //if (Parent != null)
                //{
                //    Parent.IsChecked = IsChecked ? true :Parent.IsChecked;
                //}
            }
        }
    }

上面的注釋代碼是我遇到錯誤的地方。

任何指導將不勝感激。

根據OP已經編寫的答案,再詳細一點的答案

    public bool IsChecked
    {
        get { return _isChecked; }
        set
        {
            _isChecked = value;

            if (_parent == null)
            {
                foreach (MenuItem Child in _subItems)
                {
                    Child._isChecked = this._isChecked;
                    Child.RaisePropertyChanged("IsChecked");
                }
            }

            if (_parent != null)
            {
                _parent.NotifyChecked(_isChecked);
            }

            RaisePropertyChanged("IsChecked");
        }
    }
    public void NotifyChecked(bool childChecked) 
    { 
       _isChecked = childChecked;
        RaisePropertyChanged("IsChecked"); 
       if (_parent != null)
       {
           _parent.NotifyChecked(_isChecked);
       }
    }

我認為,如果選中其中一個孩子,則需要其他屬性來存儲。 類似於IsChildChecked。

在UI中,您可以使用MultiBinding將這兩個屬性(IsChecked和IsChildChecked)綁定到節點的IsChecked。 使用轉換器進行設置。

機器學習的評論使我得到了答案:

public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;

                if (_parent == null)
                {
                    foreach (MenuItem Child in _subItems)
                    {
                        Child._isChecked = this._isChecked;
                        Child.RaisePropertyChanged("IsChecked");
                    }
                }

                if (_parent != null)
                {
                     _parent._isChecked = _isChecked ? true : _parent._isChecked;
                    _parent.RaisePropertyChanged("IsChecked");
                }

                RaisePropertyChanged("IsChecked");
            }
        }

將代碼移到設置器上,而不是在發生事件時對其進行處理對我來說很有效。

您可以采取幾種不同的方法,

1計算父母的被檢查財產

這將通過父級偵聽孩子的PropertyChanged事件,然后如果它們中的任何一個為true來為父級IsChecked返回true,則將起作用

private bool isChecked;
public bool IsChecked
{
    get{ return isChecked || Children.Any(c=>IsChecked);}
    set
    {
        isChecked = value;
        RaisePropertyChanged("IsChecked");
        foreach(var child in Children)child.IsChecked
    }
}
public void Child_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
    if(e.PropertyName == "IsChecked")
        RaisePropertyChanged("IsChecked");
}

這種方法的好處是可以獨立保持父母的點擊狀態

2翻轉1回合並計算孩子的IsChecked屬性

private bool isChecked;
public bool IsChecked
{
    get{ return isChecked || Parent.IsChecked;}
    set
    {
        isChecked = value;
        RaisePropertyChanged("IsChecked");
    }
}
public void Parent_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
    if(e.PropertyName == "IsChecked")
        RaisePropertyChanged("IsChecked");
}

3創建第二條路由以在不觸發級聯的情況下更改狀態

private bool isChecked;
public bool IsChecked
{
    get{ return isChecked;}
    set
    {
        SetIsChecked( value);
        foreach(var child in Children)Parent.SetIsChecked(isChecked)
    }
}
public void SetIsChecked(bool value)
{
    isChecked = value;
    RaisePropertyChanged("IsChecked");
}

這樣,只要子級直接調用SetIsChecked方法,則級聯僅在通過設置器直接設置父級時觸發

注意:在您的代碼中,您不處理PropertyChanged事件,而僅引發它

處理看起來像這樣

public MenuItem Parent
{
    get { return _parent; }
    set
    {
        //remove old handler
        // this stops listening to the old parent if there is one
        if(_parent != null)
            _parent.PropertyChange-=Parent_PropertyChanged;

        //notice that the value of _parent changes here so _parent above is not the same as _parent used below
        _parent = value;

        //add new handler
        // this starts listening to the new parent if there is one
        if(_parent != null)
            _parent.PropertyChange+=Parent_PropertyChanged;

        RaisePropertyChanged("Parent");
    }
}
//handler
public void Parent_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
    if(e.PropertyName == "IsChecked")
        RaisePropertyChanged("IsChecked");
}

通過在進行任何更改之前檢查當前值是否已更改,上述所有內容都可以得到改善

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM