【问题标题】:How to get data into the ViewModel of a UserControl?如何将数据放入 UserControl 的 ViewModel 中?
【发布时间】:2012-03-24 16:28:55
【问题描述】:

WPF/MVVM 的新手。我有一个“MyData”类型的数据对象。它的属性之一是“MySubsetData”类型。

我在数据网格中显示“MyData”对象的集合。

<DataGrid ItemsSource="{Binding Path=MyDataCollection}">
    <!-- Each row of the datagrid contains an item of type "MyData" -->
    <DataGrid.Columns .../>
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <local:MySubsetDataUserControl/>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

行详细信息应显示“MySubsetData”的内容。行详细信息的视图位于单独的用户控件中(此处:“MySubsetDataUserControl”)。

目前我没有为“MySubsetDataUserControl”设置视图模型,所以它从父数据网格行继承数据上下文。

<UserControl>
    <!-- Namespace stuff not shown for simplicity -->
    <Grid DataContext="{Binding Path=MySubsetData}">
        <!-- Show the MySubsetData properties here -->
        <!-- e.g. a textbox -->
            <TextBox Text="{Binding Path=TextData, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</UserControl>

虽然这行得通,但我在使用这种方法时遇到了几个问题:

  1. 所有业务逻辑都将在用户控件父视图模型中,它根本不属于那里。使视图模型比它需要的更混乱。更不用说用户控件 xaml 中的命令绑定看起来也很丑陋。就是感觉不对。

  2. 由于可以同时看到更多行详细信息,我无法将“MySubsetData”的属性绑定到视图模型中的可观察属性。 IE。如果我更改代码中的属性(例如 TextData),则更改不会反映在视图中。我的解决方法是不更改属性“TextData”。相反,我更改了文本框 Text 属性的内容,这反过来又会更新“TextData”属性。这感觉很不对劲!

所以我想为我的用户控件使用另一个视图模型,但我不知道如何访问我的数据。

<UserControl.DataContext>
    <local:UserControlViewModel/>
</UserControl.DataContext>

我现在如何访问“MySubsetData”?

【问题讨论】:

    标签: wpf mvvm user-controls viewmodel


    【解决方案1】:

    假设你有一个这样的视图模型:

    public class ViewModel
    {
        public IEnumerable<MyData> MyDataCollection{get; private set;}
    }
    

    在哪里

    public class MyData
    {
        public MySubsetData MySubsetData { get; }
    }
    

    包含 DataGrid 的视图将是

    <UserControl.DataContext>
        <local:ViewModel>
    </UserControl.DataContext>
    
    <DataGrid ItemsSource="{Binding Path=MyDataCollection}">
        <DataGrid.Columns .../>
        <DataGrid.RowDetailsTemplate>
            <DataTemplate> 
                <!-- each row of the items control has an implicit DataContext of MyData -->
                <!-- so bind the DataContext of the subset control to MySubsetData -->
                <local:MySubsetDataUserControl DataContext={Binding MySubsetData}/>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
    

    现在您的子集控件看起来像

    <UserControl>
        <Grid>
            <TextBox Text="{Binding Path=TextData, UpdateSourceTrigger=PropertyChanged}"/>
        </Grid>
    </UserControl>
    

    在重新阅读问题时,也许我所做的只是以稍微不同的方式重复问题中的 Xaml。

    但是各种类应该实现 INotifyPropertyChanged,例如

    public class MySubsetData : INotifyPropertyChanged
    {
       public string TextData
       {
          get{...}
          set{...; OnPropertyChanged("TextData"); }
       }
    }
    

    然后绑定到 TextData 属性的 TextBox 将反映代码中所做的更改。

    【讨论】:

    • 您的回答正确地描述了我的设置。这就是我目前正在做的事情。您只需在调用本身中设置用户控件上下文,而我在用户控件网格中执行此操作。所以你的更优雅,但没有回答我的问题。
    • 您说'如果我更改代码中的属性(例如 TextData),更改将不会反映在视图中'。如果视图模型实现 INotifyPropertyChanged 并且 TextData 属性引发 OnPropertyChanged("TextData"),则会反映出来。
    • 这正是重点。假设我有两个可见的行详细信息。一行显示“MyData1.MySubsetData.TextData”。另一个显示“MyData2.MySubsetData.TextData”。所以数据网格视图模型不能有一个可观察的 TextData 属性。或者我的错误在哪里?
    • 好的。只需阅读您编辑的答案。所以数据容器是问题所在。有趣的。从来没想过。可能需要围绕它编写一个包装类。这需要一些时间。那么用户控件的单独视图模型是不可行的?
    • 如果用户控件有自己的视图模型,那么您需要向控件添加依赖属性。您将绑定到这些属性,并且 dp 事件处理程序将更新视图模型。不过有点复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 2021-06-08
    • 1970-01-01
    • 2017-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多