简体   繁体   中英

C# MVVM Pass through ObservableCollection with ContentPresenter and Bind in Custom Control

I am trying to create a WPF MVVM application that shows me a list of groups. The example is kept extra simple, for the described case it is probably too complicated. For this I created three UserControls:

  • SideMenuControl (should show a TextBox and the list of groups)
  • GroupListControl (should contain the single groups)
  • GroupControl (should show the name of the group)

It should looks like this:

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

However, I don't understand how to pass the ObservableCollection Groups to the GroupListControl. I guess it must succeed with DataContext , Content and/or ItemSource . Can anyone help me further?

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="TrayTool" 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();
        }
    }
}

EDIT

If I replace the ContentPresenter element with the ScrollViewer element from GroupListControl.xaml (and remove the word Groups from the Binding Element) in SideMenuControl.xaml like below, I get the result that I want. But how can I achieve this with UserControls (GroupListControl and GroupControl)?

SideMenuControl.xaml (modified)

<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>

It seems like what you want is a DependencyProperty on your GroupListControl . Eg, within your GroupListControl.cs file:

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);
}

You can then bind to this in your XAML whenever you use a GroupListControl .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM