Search code examples
c#wpficommand

UserControl with command-property


I have a UserControl for a LinkLabel with an Image before.

The XAML looks like:

<UserControl>
    <StackPanel Orientation="Horizontal">
        <Image Source="{Binding Source={x:Static helper:ImageHelper.JumpLabelImage}}" Width="16" Height="16" VerticalAlignment="Center"/>
        <TextBlock >
            <Hyperlink Command="{Binding JumpCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                       CommandParameter="{Binding CommandParameter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
                <TextBlock Text="{Binding LabelText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" VerticalAlignment="Center" />
            </Hyperlink>
        </TextBlock>
    </StackPanel>
</UserControl>

The DataContext of this UserControl is set to the CodeBehind-File.

The Code-Behind-File looks like:

public partial class JumpLabel : UserControl
{
    public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText", typeof(string), typeof(JumpLabel));

    public static readonly DependencyProperty JumpCommandProperty = DependencyProperty.Register("JumpCommand", typeof(ICommand), typeof(JumpLabel));

    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(JumpLabel));

    public string LabelText
    {
        get { return (string)GetValue(LabelTextProperty); }
        set { SetValue(LabelTextProperty, value); }
    }

    public ICommand JumpCommand
    {
        get { return (ICommand)GetValue(JumpCommandProperty); }
        set { SetValue(JumpCommandProperty, value); }
    }

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public JumpLabel()
    {
        InitializeComponent();
    }
}

Now I want to call the JumpCommand if the user clicks on the LinkLabel

Therefor I use the following code to assign the Command in the view of my MainWindow:

<view:JumpLabel LabelText="Extensions" JumpCommand="{Binding JumpLabelCommand, UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding ElementName=control}"/>

In the ViewModel of my MainWindow I have:

private ICommand _jumpLabelCommand;
public ICommand JumpLabelCommand
{
    get { return _jumpLabelCommand; }
    set
    {
        _jumpLabelCommand = value; 
        OnPropertyChanged();
    }
}

and

public MainWindowViewModel()
{
    _mainWindowModel = new MainWindowModel();
    JumpLabelCommand = new RelayCommand(DummyExecute);
}

private void DummyExecute(object parameter)
{

}

In the DummyExecute I have a Breakpoint which is never reached. I'm not getting it why my Command doesn't work. What am I doing wrong?


Update:

I've created a new small project with focus on the binding-problem for the Command-Property in the UserControl.

The MainWindowView is:

<Window x:Class="UCWithDP.View.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModel="clr-namespace:UCWithDP.ViewModel"
        xmlns:view="clr-namespace:UCWithDP.View"
        Title="MainWindowView" Height="300" Width="600">
    <Window.DataContext>
        <viewModel:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Content="{Binding SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <view:JumpLabel Grid.Row="1" JumpLabelText="My Jump Label" JumpCommand="{Binding DoJumpCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <Button Grid.Row="2" Content="Some Button" Command="{Binding DoJumpCommand}"/>
    </Grid>
</Window>

The MainWindowViewModel is:

 internal class MainWindowViewModel : ViewModelBase
{
    private string _someText;
    private ICommand doJumpCommand;

    public MainWindowViewModel()
    {
        SomeText = "Hello from ViewModel";
        DoJumpCommand = new RelayCommand(DoJumpExecute);
    }

    public string SomeText
    {
        get { return _someText; }
        set
        {
            _someText = value;
            OnPropertyChanged();
        }
    }

    public ICommand DoJumpCommand
    {
        get { return doJumpCommand; }
        set
        {
            doJumpCommand = value;
            OnPropertyChanged();
        }
    }

    private void DoJumpExecute(object parameter)
    {

    }
}

My UserControl is:

<UserControl x:Class="UCWithDP.View.JumpLabel"
             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" 
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Height="20"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             x:Name="uc">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="X" FontWeight="Bold" VerticalAlignment="Center" Margin="2"/>
        <TextBlock Grid.Column="1" Margin="2">
            <Hyperlink Command="{Binding ElementName=uc, Path=JumpCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <TextBlock Text="{Binding JumpLabelText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center"/>
            </Hyperlink>
        </TextBlock>
    </Grid>
</UserControl>

The code-behind of the UserControl is

public partial class JumpLabel : UserControl
{
    public static readonly DependencyProperty JumpLabelTextProperty = DependencyProperty.Register(
        "JumpLabelText", typeof (string), typeof (JumpLabel), new PropertyMetadata(default(string)));

    public static readonly DependencyProperty JumpCommandProperty = DependencyProperty.Register(
        "JumpCommand", typeof (ICommand), typeof (JumpLabel), new PropertyMetadata(default(ICommand)));

    public JumpLabel()
    {
        InitializeComponent();
    }

    public ICommand JumpCommand
    {
        get { return (ICommand) GetValue(JumpCommandProperty); }
        set { SetValue(JumpCommandProperty, value); }
    }

    public string JumpLabelText
    {
        get { return (string) GetValue(JumpLabelTextProperty); }
        set { SetValue(JumpLabelTextProperty, value); }
    }
}

In the MainWindowView the Command-Property of my UserControl and a Button are binded to the same ICommand. If I click the Button my Breakpoint in DoJumpExecute is reached. If I click on the HyperLink the Breakpoint is not reached.

I still don't understand it...


Solution

In my MainWindowView now I use the following code

  <view:JumpLabel Grid.Row="1" JumpLabelText="My Jump Label" 
                        JumpCommand="{Binding DataContext.DoJumpCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged,
            RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>

and now it works.


Solution

  • Judging by the code that you have provided, you could have any number of problems... assuming that you actually have all of the properties that you are using correctly defined, these are the most likely causes of error:

    Firstly, when data binding from a UserControl XAML page to its properties, you should get used to using the RelativeSource Binding, despite its verbosity. Note that you should do this instead of setting the UserControl.DataContext to its code behind:

    <Hyperlink Command="{Binding JumpCommand, Mode=TwoWay, 
        UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding CommandParameter, 
        RelativeSource={RelativeSource AncestorType={x:Type YourPrefix:YourUserControl}}, 
        Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <TextBlock Text="{Binding LabelText, RelativeSource={RelativeSource AncestorType={
            x:Type YourPrefix:YourUserControl}, UpdateSourceTrigger=PropertyChanged, 
            Mode=TwoWay}" VerticalAlignment="Center" />
    </Hyperlink>
    

    Next, you have this line of code:

    <view:JumpLabel LabelText="Extensions" JumpCommand="{Binding JumpLabelCommand, 
        UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding ElementName=
        control}" />
    

    This will of course not work if any of the following conditions are true:

    1. You don't have an ICommand property named JumpLabelCommand in your bound view model, or code behind.
    2. You don't have a UI control named control in your view.
    3. The DataContext of the UI control named control does not have a suitable value to use as the CommandParameter property... perhaps this Binding should have been: CommandParameter="{Binding Propertyname, ElementName=control}"?

    If none of the above conditions are true and you are still having problems, then please edit your question and provide all of the relevant code, which should include everything that is relevant, eg. details of the control element, it's set DataContext, etc.