-1

I am trying to define a user control for the typical dual list situation (where there are two lists of items side by side and button controls to cause selected items from one to be transferred to the other). I am not very proficient at WPF -- most of what I've learned has been bits and pieces through sites like this. I have learned that I can create custom dependency properties for the control so that I can defer binding of items in the control (buttons, textboxes, etc.) until the control is actually used which is great. However, for my control I am going to have the two lists (probably DataGrids since most of my code, to date, has involved them) but they will require a lot more than binding so what I would like to do is something like this:

    <MyUserControl . . . .>
        <DataGrid . . . .>
        <DataGrid . . . .>
    </MyUserControl>

But I have no idea how to make that work. I thought there might be some way I could use ContentControls as a stand-in for the DataGrids and then somehow link the datagrids back to the contentcontrols in the usercontrol but I don't really understand Contentcontrols and none of the examples I found using them seemed to apply at all to what I want to do.

Can anyone point me in the right direction on this? Thank you.

Les N
  • 11
  • 4
  • Your code probably doesn't "require a lot more than binding" - if your data is bound to a viewmodel, then the model can implement any level of complexity you require, and reflect changes in state back to the UI via binding. – Simon MᶜKenzie Apr 28 '17 at 02:12
  • Thank you for your comment, but how do I address things like the number and size of columns, headings, formatting of columns, etc. from the view-model? I understand that I can autogenerate columns but that doesn't give me much control over them and most of these concerns are purely view-related so it doesn't seem likely they should be done in the view-model. – Les N Apr 28 '17 at 16:07
  • You make a good point, and I really wasn't in a position to judge your problem, but it makes sense to me that the viewmodel should have responsibility for the number of columns. [This answer](http://stackoverflow.com/a/4379965/622391) achieves it through binding. I'd agree that the formatting is really the view's concern, so I'd define all columns in the xaml but let the viewmodel control their visibility. – Simon MᶜKenzie Apr 28 '17 at 22:03

1 Answers1

0

I did some more research and found a promising approach, here: How to add controls dynamically to a UserControl through user's XAML?

I did a small proof-of-concept project and it worked great so I thought I would share it here. It uses Galasoft's MVVM Light framework.

I created a user control with a textblock, a ContentControl, and a button:

    <UserControl x:Class="DataGridInUserControlDemo.UserControls.DGPlusUC"
            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:ignore="http://www.galasoft.ch/ignore"
            mc:Ignorable="d ignore"
            x:Name="ControlRoot">
        <Grid DataContext="{Binding ElementName=ControlRoot}" Margin="10, 10, 10, 10" MinHeight="300" MinWidth="300">
            <Grid.RowDefinitions>
                <RowDefinition Height="1*"/>
                <RowDefinition Height="5*"/>
                <RowDefinition Height="2*"/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Text="{Binding Path=BannerText}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
            <ContentControl Grid.Row="1" Content="{Binding Path=InnerContent}" />
            <Button Grid.Row="2" x:Name="DemoButton" Content="{Binding Path=ButtonContent}" Command="{Binding Path=ButtonCommand}" Width="75" Height="25" HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Grid>
    </UserControl>

The attributes I wish to bind external to the UserControl are themselves bound to custom DependencyProperty(s).

This is the code-behind for the user control containing the DependencyProperty definitions:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    namespace DataGridInUserControlDemo.UserControls
    {
        public partial class DGPlusUC : UserControl
        {
            public DGPlusUC()
            {
                InitializeComponent();
            }

            public const string BannerTextPropertyName = "BannerText";

            public string BannerText
            {
                get
                {
                    return (string)GetValue(BannerTextProperty);
                }
                set
                {
                    SetValue(BannerTextProperty, value);
                }
            }

            public static readonly DependencyProperty BannerTextProperty = DependencyProperty.Register(
                BannerTextPropertyName,
                typeof(string),
                typeof(DGPlusUC));

            public const string ButtonContentPropertyName = "ButtonContent";

            public string ButtonContent
            {
                get
                {
                    return (string)GetValue(ButtonContentProperty);
                }
                set
                {
                    SetValue(ButtonContentProperty, value);
                }
            }

            public static readonly DependencyProperty ButtonContentProperty = DependencyProperty.Register(
                ButtonContentPropertyName,
                typeof(string),
                typeof(DGPlusUC));

            public const string ButtonCommandPropertyName = "ButtonCommand";

            public ICommand ButtonCommand
            {
                get
                {
                    return (ICommand)GetValue(ButtonCommandProperty);
                }
                set
                {
                    SetValue(ButtonCommandProperty, value);
                }
            }

            public static readonly DependencyProperty ButtonCommandProperty = DependencyProperty.Register(
                ButtonCommandPropertyName,
                typeof(ICommand),
                typeof(DGPlusUC));

            public const string InnerContentPropertyName = "InnerContent";

            public UIElement InnerContent
            {
                get
                {
                    return (UIElement)GetValue(InnerContentProperty);
                }
                set
                {
                    SetValue(InnerContentProperty, value);
                }
            }

            public static readonly DependencyProperty InnerContentProperty = DependencyProperty.Register(
                InnerContentPropertyName,
                typeof(UIElement),
                typeof(DGPlusUC));
        }
    }

This is the XAML for the main window:

    <Window x:Class="DataGridInUserControlDemo.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:ignore="http://www.galasoft.ch/ignore"
            xmlns:demo="clr-namespace:DataGridInUserControlDemo.UserControls"
            mc:Ignorable="d ignore"
            Height="400"
            Width="400"
            Title="MVVM Light Application"
            DataContext="{Binding Main, Source={StaticResource Locator}}">

        <Window.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Skins/MainSkin.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Window.Resources>
        <Grid x:Name="LayoutRoot">
            <demo:DGPlusUC BannerText="{Binding UCTitle}"  ButtonContent="{Binding ButtonText}" ButtonCommand="{Binding ButtonCommand}" HorizontalAlignment="Center" VerticalAlignment="Center">
                <demo:DGPlusUC.InnerContent>
                    <Grid DataContext="{Binding Main, Source={StaticResource Locator}}">
                        <DataGrid ItemsSource="{Binding Path=DataItems}" AutoGenerateColumns="True" />
                    </Grid>
                </demo:DGPlusUC.InnerContent>
            </demo:DGPlusUC>
        </Grid>
    </Window>

The custom DependencyProperty(s) are used as tags in the control to bind to the properties in the ViewModel.

Note the bracketed demo:DGPlusUC.InnerContent -- this is where the replacement code for the ContentControl goes. The embedded UserControl inherits the Window's datacontext but for some reason this section did not, so, after experimenting with it a bit, I just threw up my hands and declared the DataContext explicitly.

And here is the ViewModel code:

    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.CommandWpf;
    using DataGridInUserControlDemo.Model;
    using System.Windows.Input;
    using System.Collections.ObjectModel;

    namespace DataGridInUserControlDemo.ViewModel
    {
        public class MainViewModel : ViewModelBase
        {
            private readonly IDataModel theModel;

            private ObservableCollection<DataItem> _DataItems;

            public ObservableCollection<DataItem> DataItems
            {
                get
                {
                    return _DataItems;
                }

                set
                {
                    if (_DataItems == value)
                    {
                        return;
                    }

                    var oldValue = _DataItems;
                    _DataItems = value;
                    RaisePropertyChanged(() => DataItems, oldValue, value, true);
                }
            }


            private string _ButtonText = "First";
            public string ButtonText
            {
                get
                {
                    return this._ButtonText;
                }

                set
                {
                    if (this._ButtonText == value)
                    {
                        return;
                    }

                    var oldValue = this._ButtonText;
                    this._ButtonText = value;
                    RaisePropertyChanged(() => ButtonText, oldValue, value, true);
                }
            }


            private string _UCTitle = string.Empty;
            public string UCTitle
            {
                get
                {
                    return this._UCTitle;
                }

                set
                {
                    if (this._UCTitle == value)
                    {
                        return;
                    }

                    var oldValue = this._UCTitle;
                    this._UCTitle = value;
                    RaisePropertyChanged(() => UCTitle, oldValue, value, true);
                }
            }

            private ICommand _ButtonCommand;
            public ICommand ButtonCommand
            {
                get
                {
                    return this._ButtonCommand;
                }

                set
                {
                    if (this._ButtonCommand == value)
                    {
                        return;
                    }

                    var oldValue = this._ButtonCommand;
                    this._ButtonCommand = value;
                    RaisePropertyChanged(() => ButtonCommand, oldValue, value, true);
                }
            }

            public MainViewModel(IDataModel model)
            {
                this.theModel = model;
                this._UCTitle = "DataGrid in User Control Demo";
                this._DataItems = new ObservableCollection<DataItem>(this.theModel.SomeData);
                this._ButtonCommand = new RelayCommand(this.ButtonCmd, () => { return true; }) ;
            }

            private void ButtonCmd()
            {
                if (this.ButtonText == "First")
                {
                    this.ButtonText = "Second";
                }
                else
                {
                    this.ButtonText = "First";
                }
            }
        }
    }

Finally, here are the results:

DataGrid in UserControl Demo

Community
  • 1
  • 1
Les N
  • 11
  • 4