【问题标题】:C# MVVM Pass through ObservableCollection with ContentPresenter and Bind in Custom ControlC# MVVM 通过 ObservableCollection 与 ContentPresenter 并在自定义控件中绑定
【发布时间】:2021-11-01 16:36:04
【问题描述】:

我正在尝试创建一个向我显示组列表的 WPF MVVM 应用程序。该示例保持非常简单,对于所描述的情况,它可能太复杂了。为此,我创建了三个 UserControl:

  • SideMenuControl(应该显示一个文本框和组列表)
  • GroupListControl(应包含单个组)
  • GroupControl(应该显示组的名称)

应该是这样的:

 ________________________
| SideMenuControl        |
|                        |
| Hello World            |
|  ____________________  |
| |GroupListControl    | |
| |                    | |
| |  ________________  | |
| | | Group1         | | |
| | | Group2         | | |
| | |________________| | |
| |____________________| |
|________________________|

但是,我不明白如何将 ObservableCollection Groups 传递给 GroupListControl。我想它必须通过 DataContextContent 和/或 ItemSource 成功。谁能进一步帮助我?

GroupControl.xaml

<UserControl x:Class="WpfApp1.GroupControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:WpfApp1"
         mc:Ignorable="d" 
         d:DesignHeight="30" d:DesignWidth="300">
  <TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center"/>
</UserControl>

GroupListControl.xaml

<UserControl x:Class="WpfApp1.GroupListControl"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d" 
            d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ScrollViewer VerticalScrollBarVisibility="Auto">
            <ItemsControl ItemsSource="{Binding Groups}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:GroupControl />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer> 
    </Grid>
</UserControl>

SideMenuControl.xaml

<UserControl x:Class="WpfApp1.SideMenuControl"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d" 
            d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <TextBox Grid.Row="0" Text="Hello World"/>
                <ContentPresenter Grid.Row="1"/>
            </Grid>
        </ControlTemplate>
    </UserControl.Template>    
</UserControl>

GroupViewModel.cs

using Microsoft.Toolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfApp1
{
    public class GroupViewModel : ObservableObject
    {
        private Group _group;
        public string Name
        {
            get => _group.Name;
            set => SetProperty(_group.Name, value, _group, (model, name) => model.Name = name);
        }

        public GroupViewModel(Group model)
        {
            _group = model;
        }
    }
}

GroupCollectionViewModel.cs

using Microsoft.Toolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfApp1
{
    public class GroupCollectionViewModel : ObservableObject
    {
        public ObservableCollection<GroupViewModel> Groups { get; set; }

        public GroupCollectionViewModel()
        {
            Groups = new ObservableCollection<GroupViewModel>();
            Groups.Add(new GroupViewModel(new Group { Name = "Group1" }));
            Groups.Add(new GroupViewModel(new Group { Name = "Group2" }));
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="GroupList" Height="450" Width="800">
    <local:SideMenuControl
        DataContext="{Binding Groups}">
    </local:SideMenuControl>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new GroupCollectionViewModel();
        }
    }
}

编辑

如果我用 GroupListControl.xaml 中的 ScrollViewer 元素替换 ContentPresenter 元素(并删除单词 Groups 来自 SideMenuControl.xaml 中的绑定元素),如下所示,我得到了我想要的结果。但是如何使用 UserControls(GroupListControl 和 GroupControl)实现这一点?

SideMenuControl.xaml(修改)

<UserControl x:Class="WpfApp1.SideMenuControl"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d" 
            d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <TextBox Grid.Row="0" Text="Hello World"/>
                <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
                    <ItemsControl ItemsSource="{Binding}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <local:GroupControl />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
            </Grid>
        </ControlTemplate>
    </UserControl.Template>    
</UserControl>

【问题讨论】:

  • 如果不查看您实际在哪里实例化 GroupListControl,您的问题很难理解。我也不明白你的文件命名,例如。 “GroupListControl.cs”,其中文件是 xaml 模板,而不是 cs 代码 - 应该是 GroupListControl.xaml?您可以在 GroupListControl.cs 的视图模型中放置一个 ObservableCollection 并在初始化 GroupListControl 时将数据放入其中吗?
  • 感谢您的建议,我已经更正了文件名。 GroupListControl 没有直接实例化,也许这就是问题所在。我不知道这应该在哪里完成。我在 GroupCollectionViewModel 中有一个 ObservableCollection,所以我不明白你的建议。
  • 我的意思是:在您共享的代码中,您从未真正使用过 GroupListControl。 Usercontrol 没有放置在任何 XAML 模板中(这也将实例化它。)我也对此感到困惑:public ObservableCollection&lt;GroupViewModel&gt; Groups { get; set; } GroupViewModel 根据名称应该是一个 ViewModel,但您似乎只是在使用它作为您的组数据的容器,与任何视图无关。命名背后有什么原因吗?如果您使用视图的 ViewModel(例如 GroupListControl 类),您的问题应该是微不足道的 :)
  • ObservableCollection Groups 已绑定 MainWindow.xaml: 。我想我需要在此 SideMenuControl 元素中将 ContentTemplate 添加到 GroupListControl。但到目前为止,我还没有成功。
  • 从哪里传过来的?你的问题到底是什么?你能把它缩小一点吗?你不会“传递”任何东西给UserControl

标签: c# wpf mvvm


【解决方案1】:

看来您想要的是GroupListControl 上的DependencyProperty。例如,在您的 GroupListControl.cs 文件中:

public static readonly DependencyProperty GroupsProperty = 
    DependencyProperty.Register(
        "Groups",
        typeof(ObservableCollection<GroupViewModel>),
        typeof(GroupListControl)
    );

public ObservableCollection<GroupViewModel> Groups
{
    get => (ObservableCollection<GroupViewModel>)GetValue(GroupsProperty);
    set => SetValue(GroupsProperty, value);
}

然后,只要您使用 GroupListControl,您就可以在 XAML 中绑定到它。

【讨论】:

  • 对我来说似乎是一种不同的方法,我还没有追求过。不过,我也错过了这里的联系。我究竟在哪里实例化 GroupListControl?
  • 你可以在任何你想使用它的地方实例化它,例如,在窗口、页面、用户控件等中。这有帮助吗?
  • 你能把它和我的例子联系得更具体一点吗?我将在哪里指定 GroupListControl?在 SideMenuControl 而不是 ContentPresenter 中?还是作为 ContentPresenter 的子元素?还是直接在MainWindow中作为SideMenuControl元素的子元素?
  • SideMenuControl 中是否需要 ContentPresenter?您打算在哪里/如何指定内容?
  • 其实没有必要。我正在尝试学习 MVVM 和 XAML,但我一无所获。在某处我必须指定我想在 ContentPresenter 中使用 GroupListControl 显示我的 DataContext(组)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-15
  • 1970-01-01
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多