WPF中使用数据绑定刷新UI

3
我有一个三层深的树形视图,其中 > 和 X 表示特定项的状态(从后端确定)。
我正在使用 ObservableDictionary 来绑定此树形结构(它具有 ICollectionChanged 事件)。结构如下:
ObservableDictionary<string,CustomClass> mainitems; public class CustomClass{ ObservableDictionary<string, InnerClass> sub1item; // 此类中有一堆属性和方法 // INotify 没有实现 } public class InnerClass{ // 此类中有一堆属性和方法 // INotify 没有实现 public SomeEnum Status{ get{ return this.status; } } }
上面提到的图形是使用自定义转换器绑定的,该转换器将 Status 枚举转换为路径,以便可以进行绑定(即 <img source="{Binding Path=something, Converter={StaticResource someconverter}, Mode=OneWay" /> )。
我的问题是,当我使用新状态更新 CustomClass 的 sub1item 字典时,它不会在 UI 中更新。我认为实现 INotify 可能有效,但我不知道需要在哪里更新它以及如何确切地执行。
编辑:我的树形视图的 XAML 模板如下:

<TreeView Name="tvInstance" ItemsSource="{Binding}" TreeViewItem.Selected="tviSelected" IsTextSearchEnabled="True">
    <TreeView.ItemContainerStyle>
        <Style>
            <Setter Property="TreeViewItem.IsExpanded" Value="{Binding Path=Value.Expanded, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Value.CustomClass}" ItemContainerStyle="{x:Null}">
                <StackPanel Orientation="Horizontal">
                <Label Content="{Binding Path=Key}"/>
            </StackPanel>
            <HierarchicalDataTemplate.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=Value.AnotherClass}">
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Path=Value.Status, Converter={StaticResource convertstatus} }"
                            Width="10" Height="10"/>
                        <Label Content="{Binding Path=Key}" />
                    </StackPanel>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding Path=Value, Converter={StaticResource convertstatus} }"
                            Width="10" Height="10"/>
                                <Label Content="{Binding Path=Key}" />
                            </StackPanel>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

编辑:在我的主类、CustomClass 和 InnerClass 中添加所有 INotifyPropertyChanged 事件后,它仍然无法工作。我正在使用 ObservableDictionary 的 Dr. WPF 版本(由于我需要进行大量查找,所以使用字典非常重要)。请帮忙!

结语

这个页面上的答案是正确的,因为需要在 UI 中更新的属性上实现 INotifyPropertyChanged。

我发现绑定字典太麻烦了,所以我保留了 ObservableCollection 和 Dictionary。我使用字典进行查找,使用集合进行绑定(因为两者都使用对象的相同引用,使用集合很容易删除且只需 O(n) 操作)。

关于在 UI 中更新,请参考本页其他帖子。


你是否重新实例化了sub1item字典?如果是的话,那么你需要在包含它的类中实现INotifyPropertyChanged。 - rmoore
CustomClass有一个Refresh()方法,它设置sub1item = new ObservableDictionary<..>();然后再次获取所有InnerClass项。但是,它们几乎是相同的项。我只想更新已更改的那些项。即. 向上渗透更改。 - apandit
你有找到解决这个问题的方法吗? - Zenuka
点赞,因为你花时间写了那个结语。击掌庆祝! - Rob Fonseca-Ensor
5个回答

1

这可能有点长,以下是我最好的猜测:

public class CustomClass : INotifyPropertyChanged
{
  public CustomClass()
  {
    sub1item = new ObservableDictionary<string, InnerClass>();
    // This next line may not be necessary... Changes might propogate up.
    sub1item.CollectionChanged += () => NotifyPropertyChange("Sub1Item");
  }

  private ObservableDictionary<string, InnerClass> sub1item;
  public ObservableDictionary<string, InnerClass> Sub1Item
  {
    get { return sub1item; }
    private set { sub1item = value; NotifyPropertyChange("Sub1Item"); }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void NotifyPropertyChanged(String info)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
  }
}

public class InnerClass : INotifyPropertyChanged
{
  public SomeEnum Status
  {
    get { return this.status; }
    private set { this.status = value; NotifyPropertyChange("Status"); }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void NotifyPropertyChanged(String info)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
  }
}

请确保通过调用 Status = something 更新您的状态,而不是直接通过 this.status 进行更新。

编辑:如果您只想更新获得更新状态的单个对象,我不确定这样做是否可行。我怀疑这将标示 Sub1Item 已更改,但 mainitems 可能不知道该个别对象。这取决于您的实现方式。

如果您为 CustomClass 创建了一个 DataTemplate,并对 Sub1Item 进行了绑定,则您的绑定将仅为更新的状态正确更新。

<DataTemplate DataType="{x:Type myClrNamespace:InnerClass}">
    <Grid>
        <TextBlock Text={Binding Path=Status}/>
    </Grid>
</DataTemplate>
...
<ListBox x:Name="listStatus"/>

然后在 C# 中的某个地方,您可以添加这段代码:listStatus = mainlist[0].Sub1Item; 然而,在看了您的 TreeView ItemTemplate 示例之后,我不太确定了。

我已经尝试了所有的方法,但是无法添加sub1item.CollectionChanged +=...这行代码,因为我的字典的CollectionChanged事件是受保护的。另外,我注意到你使用了NotifyChange和NotifyPropertyChanged,我猜它们是一样的...还有其他的想法吗? - apandit

1

可观察集合实现了INofityCollectionChanged,WPF使用它来刷新视图项的集合。

然而,为了更新状态,您需要让数据实现INotifyPropertyChanged

  • 您想要在视图中显示的每个类都必须实现它,这样WPF就会知道其属性何时更改以及哪个属性已更改。

实现很简单...

// Should implement INotifyPropertyChanged if the dictionary itself
// can be changed and not only its items
public class CustomClass {
    ObservableDictionary sub1item;
    // Bunch of properties and methods in this class
    // INotify not implemented
}


public class InnerClass : INotifyProperyChanged {
    // Bunch of properties and methods in this class
    // INotify not implemented
    public SomeEnum Status{
        get{ return this.status; }
    }

    public event PropertyChangedEventHandler PropertyChanged;



protected void NotifyPropertyChanged(string propertyName)
{
    if(PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}


// where ever this.status is changed directly,
// call NotifyPropertyChanged("Status")
// (at end of that method)
//
// if this.status is changed from outside class (if public),
// then add a public method NotifyStatusChanged() which calls
// NotifyPropertyChanged("Status")
//
// If Status property has a set{} then if new value != this.status,
// call NotifyPropertyChanged("Status") at end of setter

}

0

类型为“任务”的类的工作示例

public class Task: INotifyPropertyChanged

{

    //Implemented from INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

私有字符串文本;

    public string Text
    {
        get { return text; }
        set { 
            text = value;
            NotifyPropertyChanged("Text");

            }
    }

}

值得一提的是,在数据绑定到类型集合时,需要使用ObservableCollection而不是List才能获得动态更新的ItemSource。List不会发出通知。

0

你需要使用一个事件,让你的类实现INotifyPropertyChanged接口,代码大概是这个样子的:

public class InnerClass: INotifyPropertyChanged
{
     private string _propertyName;

     //Implemented from INotifyPropertyChanged
     public event PropertyChangedEventHandler PropertyChanged;

     public string PropertyName
     {
        get { return _propertyName; }
        set 
        { 
              _propertyName = value;
              OnPropertyChanged("Name or Property Data"); 
        }
     }

     //Just using string as an example, send whatever data you'd like
     protected void PropertyChanged(string name)
     {
        //Check to make sure the event is wired.
        if(PropertyChanged != null)
        {
              //Fire event
              PropertyChanged(this, name);
        }
     }
}

基本上,让这些事件为您的子项触发并传递到您的CustomClass对象。然后,如果需要,让CustomClass处理这些事件,并向您的主对象触发另一个事件,告诉它更新UI。


0

ObservableDictionary(Of TKey, TValue) - VB.NET

常规功能列表:

  • ObservableDictionary(Of TKey, TValue)
  • 一次性获取AddRange通知。
  • 泛型EventArgs(Of TKey, TValue)
  • NotifyDictionaryChanging(Of TKey, TValue) - 一个CancelEventArgs的子类,允许取消操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接