Listview UWP、MVVM和级联列表视图

Listview UWP、MVVM和级联列表视图,listview,mvvm,uwp,observablecollection,Listview,Mvvm,Uwp,Observablecollection,我对UWP和两个ListView越来越着迷了。这个场景很简单:我在第二页中有一个listview(listview和listViewField) 我有一个ViewModel,如: public class FormViewModel : BaseViewModel { public ObservableCollection<TableInfo> Tables; } 第二次更新 正如@MZetko所建议的,我添加了绑定x:Bind vm.Tables,Mode=OneWay,

我对UWP和两个ListView越来越着迷了。这个场景很简单:我在第二页中有一个listview(listview和listViewField)

我有一个ViewModel,如:

public class FormViewModel : BaseViewModel {
    public ObservableCollection<TableInfo> Tables;
}
第二次更新 正如@MZetko所建议的,我添加了绑定
x:Bind vm.Tables,Mode=OneWay
,当我添加新项目时,列表视图会更新

最后一个问题是如何将所选项目与表单连接(第3点)

然后,我应该在表单中包含来自模型listview的数据

<StackPanel x:Name="stackTable" Orientation="Vertical" Padding="10">
    <TextBlock>Table name</TextBlock>
    <TextBox x:Name="textboxTableName" 
             Text="{Binding ElementName=listView, Path=SelectedItem.Name}" />
    <TextBlock>Project namespace</TextBlock>
    <TextBox x:Name="textboxNameSpace" 
             Text="{Binding ElementName=listView, Path=SelectedItem.ProjectNameSpace}" />
</StackPanel>

表名
项目名称空间
这段代码显示表单中的项,一个用于表,另一个用于字段。现在我的问题是:如果我想添加一条新记录或删除一条记录,我该怎么做

我四处看看,发现了那些链接

但我不清楚如何在我的案例中使用它


提前感谢您的帮助

第二个ListView绑定到TableInfo的Fields属性,该属性的类型为
List
。列表不支持数据绑定更新UI所依赖的更改通知

列表
更改为
可观察收集
,您将看到更好的结果

顺便提一下,我建议只绑定第二个ListView中的ItemsSource,而不是绑定DataContext和ItemsSource(这需要两次刷新UI)。它应该简单到:

<ListView x:Name="listView" ItemsSource="{x:Bind vm.Tables}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="mdl:TableInfo">
            <TextBlock Text="{x:Bind Name}" FontSize="25" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView x:Name="listViewTable" 
          ItemsSource="{Binding ElementName=listView, Path=SelectedItem.Fields}">
   <ListView.ItemTemplate>
      <DataTemplate x:DataType="mdl:FieldInfo">
          <TextBlock Text="{x:Bind Name}" FontSize="25" />
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>


希望有帮助。

您的第二个ListView正在绑定到TableInfo的Fields属性,该属性的类型为
List
。列表不支持数据绑定更新UI所依赖的更改通知

列表
更改为
可观察收集
,您将看到更好的结果

顺便提一下,我建议只绑定第二个ListView中的ItemsSource,而不是绑定DataContext和ItemsSource(这需要两次刷新UI)。它应该简单到:

<ListView x:Name="listView" ItemsSource="{x:Bind vm.Tables}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="mdl:TableInfo">
            <TextBlock Text="{x:Bind Name}" FontSize="25" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView x:Name="listViewTable" 
          ItemsSource="{Binding ElementName=listView, Path=SelectedItem.Fields}">
   <ListView.ItemTemplate>
      <DataTemplate x:DataType="mdl:FieldInfo">
          <TextBlock Text="{x:Bind Name}" FontSize="25" />
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>


希望有帮助。

如果我理解正确,问题是第二个列表视图在添加新字段时不会更新

问题在于,字段列表的类型为
list
。UWP中的数据绑定基于
INotifyPropertyChanged
INotifyCollectionChanged
接口<代码>列表也不实现,因此无法自动通知用户界面它已更改。为此,您需要将其替换为
observetecollection
。将项目添加到此集合时,UI将自动更新为新项目

然而,不幸的是,这仍然不足以更新字段。
observedcollection
类可以观察到项目的更改,如
Add
Remove
,但不能观察其中单个项目的更改。 要做到这一点,您必须确保
FieldInfo
类实现了
INotifyPropertyChanged
,并且每当要观察属性更改时,都会调用
PropertyChanged
事件

接口的最常见实现如下所示:

public event propertychangedventhandler PropertyChanged;
私有void NotifyPropertyChanged([CallerMemberName]字符串propertyName=”“)
{
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
现在,在所有需要调用
NotifyPropertyChanged
方法的属性中:

私有字符串_description=”“;
公共字符串描述
{
得到
{
返回描述;
}
设置
{
_描述=值;
NotifyPropertyChanged();
}
}

由于
[CallerMemberAttribute]
的原因,您不必指定
propertyName
参数,它将由编译器自动填写。

如果我理解正确,问题是第二个列表视图在添加新字段时不会更新

问题在于,字段列表的类型为
list
。UWP中的数据绑定基于
INotifyPropertyChanged
INotifyCollectionChanged
接口<代码>列表也不实现,因此无法自动通知用户界面它已更改。为此,您需要将其替换为
observetecollection
。将项目添加到此集合时,UI将自动更新为新项目

然而,不幸的是,这仍然不足以更新字段。
observedcollection
类可以观察到项目的更改,如
Add
Remove
,但不能观察其中单个项目的更改。 要做到这一点,您必须确保
FieldInfo
类实现了
INotifyPropertyChanged
,并且每当要观察属性更改时,都会调用
PropertyChanged
事件

接口的最常见实现如下所示:

public event propertychangedventhandler PropertyChanged;
私有void NotifyPropertyChanged([CallerMemberName]字符串propertyName=”“)
{
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
现在,在所有需要调用
NotifyPropertyChanged
方法的属性中:

私有字符串_description=”“;
公共字符串描述
{
得到
{
返回描述;
}
设置
{
_描述=值;
NotifyPropertyChanged();
}
}
因为
[CallerMemberAttribute]
public int SelectedTableInfoIndex
{
    get { return _selectedTableInfoIndex; }
    set {
        _selectedTableInfoIndex = value;
        if (_selectedTableInfoIndex >= 0) {
            _selectedTableInfo = Tables[_selectedTableInfoIndex];
        }
    }
}
private int _selectedTableInfoIndex = -1;

public TableInfo SelectedTableInfo
{
    get {
        return (_selectedTableInfoIndex >= 0) ? Tables[_selectedTableInfoIndex] : null;
    }
    set {
        _selectedTableInfo = value;
        RaisePropertyChanged("SelectedTableInfo");
    }
}
private TableInfo _selectedTableInfo;
<StackPanel x:Name="stackTable" Orientation="Vertical" Padding="10">
    <TextBlock>Table name</TextBlock>
    <TextBox x:Name="textboxTableName" 
             Text="{Binding ElementName=listView, Path=SelectedItem.Name}" />
    <TextBlock>Project namespace</TextBlock>
    <TextBox x:Name="textboxNameSpace" 
             Text="{Binding ElementName=listView, Path=SelectedItem.ProjectNameSpace}" />
</StackPanel>
<ListView x:Name="listView" ItemsSource="{x:Bind vm.Tables}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="mdl:TableInfo">
            <TextBlock Text="{x:Bind Name}" FontSize="25" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView x:Name="listViewTable" 
          ItemsSource="{Binding ElementName=listView, Path=SelectedItem.Fields}">
   <ListView.ItemTemplate>
      <DataTemplate x:DataType="mdl:FieldInfo">
          <TextBlock Text="{x:Bind Name}" FontSize="25" />
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>
public class BaseViewModel : INotifyPropertyChanged {
    #region Property changed
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notifies the property changed.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void NotifyPropertyChanged(string propertyName, Action<bool> message) {
        if (this.PropertyChanged != null) {
            // property changed
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            message?.Invoke(this.IsValid);
        }
    }

    /// <summary>
    /// Raises the property changed.
    /// </summary>
    /// <param name="property">The property.</param>
    protected void RaisePropertyChanged(string property) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    // SetField(()=> somewhere.Name = value; somewhere.Name, value) 
    // Advanced case where you rely on another property
    protected bool SetProperty<T>(T currentValue, T newValue, Action DoSet, 
                                  [CallerMemberName] String property = null) {
        if (EqualityComparer<T>.Default.Equals(currentValue, newValue)) return false;
        DoSet.Invoke();
        RaisePropertyChanged(property);
        return true;
    }
    #endregion
}