Обновление интерфейса с привязкой данных в WPF - PullRequest
3 голосов
/ 16 июня 2009

У меня есть трехслойное дерево,

-MAIN
 ->:SUB1
   >:SUB2
   >:SUB2
 -X:SUB1
   X:SUB2
 SUB1
 SUB1

где,> и X представляют графику, обозначающую состояние этого конкретного элемента (определяется из серверной части).

Я использую Observable Dictionary для привязки к этому дереву (и у него есть событие ICollectionChanged). Структура такая:


ObservableDictionary<string,CustomClass> mainitems;

public class CustomClass{
    ObservableDictionary<string, InnerClass> sub1item;
    // Bunch of properties and methods in this class
    // INotify not implemented
}

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

Графики, упомянутые выше, связываются с помощью пользовательского конвертера, который преобразует перечисление Status в путь, чтобы его можно было связать (т. Е. image).

ВОПРОС:

Моя проблема в том, что когда я обновляю словарь подэлементов CustomClass новыми статусами, он не обновляет его в пользовательском интерфейсе. Я думаю, что реализация INotify может работать, но я не знаю, где мне нужно его обновить и как именно это сделать.

Edit: Мой шаблон 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>

РЕДАКТИРОВАТЬ: После добавления всех событий INotifyProperty в мой основной класс, мой CustomClass и мой InnerClass, он по-прежнему не работает. Я использую версию ObservableDictionary от Dr.Web (и использование словаря имеет решающее значение для моего приложения, так как мне нужно сделать много поисков). Помогите!

Эпилог

Ответы на этой странице верны в том смысле, что INotifyPropertyChanged должен быть реализован в свойствах, которые я хочу обновить в пользовательском интерфейсе.

Я обнаружил, что связывание словаря было слишком большой проблемой, поэтому я сохранил и ObservableCollection и Dictionary. Я использовал словарь для поиска и коллекцию для привязки (поскольку обе используют одну и ту же ссылку на объект, удаление было легко с помощью коллекции и единственной операции O (n)).

Что касается обновления в пользовательском интерфейсе, обратитесь к другим публикациям на этой странице.

Ответы [ 5 ]

1 голос
/ 20 июня 2009

Наблюдаемые коллекции реализуют 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

}
1 голос
/ 16 июня 2009

Это может быть немного длинно, вот мое лучшее предположение:

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 = что-то, а не напрямую через this.status

Редактировать: Если вы просто хотите обновить ТОЛЬКО один объект, который получил обновленный статус, я не уверен, что это будет сделано. Я подозреваю, что это будет сигнализировать, что Sub1Item изменился, но mainitems, вероятно, не будет знать об отдельном объекте. Это зависит от вашей реализации.

Если вы создали DataTemplate для CustomClass, который имел привязку к 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, я больше не уверен.

0 голосов
/ 26 марта 2010

рабочий пример для класса типа "Задача"

открытый класс Задача: 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 при привязке данных к коллекции типов. Список не уведомляет.

0 голосов
/ 19 ноября 2009

ObservableDictionary (Of TKey, TValue) - VB.NET

Список общих функций:

  • ObservableDictionary (Of TKey, TValue)
  • AddRange получает уведомление только один раз.
  • Общие EventArgs (Of TKey, TValue)
  • NotifyDictionaryChanging (Of ​​TKey, TValue) - подкласс класса CancelEventArgs, который позволяет отменять операцию.
0 голосов
/ 16 июня 2009

Вам нужно использовать событие, ваш класс должен реализовывать 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 обработать эти события и запустите другое событие до вашего основного объекта, сообщив ему об обновлении пользовательского интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...