Search code examples
c#.netwpfwindowsdatacontext

Define command binding in user control


I wrote user control with 2 buttons and one check box and now I want to bind Commands to data context - for each button and checkbox. But I don't know how to define command binding. I think I'll need some kind of ICommand property in User control - but how can I connect user's data context command delegate? I want to use user control to manage each item in collection like this:

<ItemsControl ItemsSource="{Binding Path=MoneyInfo}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <local:ChannelSetupControl 
            CurrentCount="{Binding Count}" 
            CoinValue="{Binding Value}"
            UpCommand="{Binding DataContextUp}" 
            DownCommand="{Binding DataContextDown}" 
            ChangeCheckboxCommand="{Binding DataContextChange}"></local:ChannelSetupControl>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>

XAML User control

<UserControl>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="3*"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding CoinValue}" TextAlignment="Center"></TextBlock>

        <TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding CurrentCount, Mode=TwoWay}" TextAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock>
        <StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
            <Button Content="+ 10" Padding="0 5"></Button>
            <Button Content="- 10" Padding="0 5"></Button>
        </StackPanel>

        <CheckBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" IsChecked="{Binding Cycling, Mode=TwoWay}" Content="recycling" VerticalContentAlignment="Center"></CheckBox>
    </Grid>
</UserControl>

and code behind and this is where I'm lost - how to define UpCommand, DownCommand and ChangeCheckboxCommand?

    public partial class ChannelSetupControl : UserControl, INotifyPropertyChanged
    {
        private int currentCount;
        private bool cycling;
        private double coinValue;

        public int Step { get; set; }

        public double CoinValue { get { return coinValue; } set { coinValue = value; NotifyPropertyChanged("CoinValue"); } }
        public int CurrentCount { get { return currentCount; } set { currentCount = value; NotifyPropertyChanged("CurrentCount"); } }
        public bool Cycling { get { return cycling; } set { cycling = value; NotifyPropertyChanged("Cycling"); } }

        public ChannelSetupControl()
        {
            InitializeComponent();
            DataContext = this;

            CurrentCount = 0;
            Step = 10;
            Cycling = false;
            CoinValue = 0;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Solution

  • First of all your ChannelSetupControl class extends UserControl, so it implicitly extends DependencyObject class. It means you can use Dependency Properties instead of implementing INotifyPropertyChanged.

    So you can define a dependency property in your ChannelSetupControl class, like this one:

    public static readonly DependencyProperty UpCommandProperty =
        DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(ChannelSetupControl));
    
    public ICommand UpCommand
    {
        get { return (ICommand)GetValue(UpCommandProperty); }
        set { SetValue(UpCommandProperty, value); }
    }
    

    At the same time in your control XAML:

    <Button Command="{Binding RelativeSource={RelativeSource Mode=Self}, Path=UpCommand, Mode=OneWay}"
            Content="+ 10" Padding="0 5" />
    

    In this way in your window XAML you can wrote:

    <local:ChannelSetupControl UpCommand="{Binding UpCommand, Mode=OneWay}" ... />
    

    You can use the same "pattern" for the other controls. Regarding ICommand, there are a lot of implementations. The one that I prefer is the so called delegate command (for a sample you can take a look here). I hope this quick explanation can help you.