bas
bas

Reputation: 14982

Set value of DependencyProperty to ViewModel property

Not sure if what I want is possible, but I also wouldn't know why it isn't.

I have a user control with a dependency property (a string) which I define in XAML e.g. as follows:

<Window ... (EngraveUnitWindow)
    DataContext = EngraveUnitViewModel
    ...
    ...

    <parameters:DoubleParameterUserControl 
        DisplayName="Exchanger Offset [deg]" 
        DataContext="{Binding ExchangerOffset}"/>

The view model 'EngraveUnitViewModel' :

public class EngraveUnitViewModel : ViewModelBase, IUnitViewModel
    ...
    ...
    public DoubleParameterViewModel ExchangerOffset { get; }

What I want to achieve, is set the value of DisplayName to ParameterName property in the DoubleParameterViewModel. So I created a Style which binds the DisplayName to the viewmodel as follows:

<UserControl.Resources>
    <Style TargetType="parameters:DoubleParameterUserControl">
        <Setter Property="DisplayName" Value="{Binding ParameterName, Mode=OneWayToSource}"/>
    </Style>
</UserControl.Resources>

The complete DoubleParameterUserControl code below:

<UserControl 
    ...
    ...
    d:DataContext="{d:DesignInstance viewModels:DoubleParameterViewModel, d:IsDesignTimeCreatable=False}"
             Margin="5">

    <UserControl.Resources>
        <Style TargetType="parameters:DoubleParameterUserControl">
            <Setter Property="DisplayName" Value="{Binding ParameterName, Mode=OneWayToSource}"/>
        </Style>
    </UserControl.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <TextBlock Grid.Column="0" Text="{Binding ElementName=DoubleParameter, Path=DisplayName}" VerticalAlignment="Center" Margin="5,0,0,0" />

        <Border Grid.Column="1" BorderThickness="1" BorderBrush="LightGray" Margin="0, 0, 5, 0">
            <TextBlock 
                VerticalAlignment="Center"
                Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                Margin="5, 0, 5, 0">
                <TextBlock.InputBindings>
                    <MouseBinding Gesture="LeftClick" Command="{Binding ShowNumpadCommand}" />
                </TextBlock.InputBindings>
            </TextBlock>

        </Border>
        <Button x:Name="_button" Grid.Column="2" MinWidth="30" MinHeight="30" Content="..." Command="{Binding ShowNumpadCommand}"/>
    </Grid>
</UserControl>

And its code behind (where I define the DependencyProp:

public partial class DoubleParameterUserControl
{
    public string DisplayName
    {
        get => (string)GetValue(DisplayNameProperty);
        set => SetValue(DisplayNameProperty, value);
    }

    public static readonly DependencyProperty DisplayNameProperty =
        DependencyProperty.Register(nameof(DisplayName), typeof(string), typeof(DoubleParameterUserControl),
            new PropertyMetadata(""));

    public DoubleParameterUserControl()
    {
        InitializeComponent();

        _button.Focus();
    }
}

For completeness, the viewmodel:

public class DoubleParameterViewModel : ViewModelBase
{
    private readonly Parameter<double> parameter;
    private double value;

    public RelayCommand ShowNumpadCommand { get; }

    public string ParameterName { get; set; }

    public double Value
    {
        get => parameter.Value;
        set
        {
            parameter.Value = value;
            Set(() => Value, ref this.value, value);
        } 
    }

    public DoubleParameterViewModel(Parameter<double> parameter)
    {
        this.parameter = parameter;

        ShowNumpadCommand = new RelayCommand(ShowNumpad);
    }

    private void ShowNumpad()
    {
        var numpadViewModel = new VirtualKeypadsViewModel(true)
        {
            ParameterName = ParameterName,
            Input = Value.ToString("F2", CultureInfo.InvariantCulture)
        };

        var numpad = new Numpad
        {
            Owner = System.Windows.Application.Current.MainWindow,
            DataContext = numpadViewModel
        };

        if (numpad.ShowDialog() == true)
        {
            Value = numpadViewModel.ResultAsDouble();
        }
    }
}

Just to be clear, the property ParameterName in the ViewModel never gets set. So in my code, I want to popup a Numpad dialog which shows the parameter name in its title bar, but the ParameterName did not receive the bound DisplayName.

I hope somebody can explain me how I can solve that. (or, that it is not possible, and why not if that would sadly be the case)

Upvotes: 0

Views: 924

Answers (1)

Mike Strobel
Mike Strobel

Reputation: 25623

It seems like DoubleParameterViewModel.ParameterName exists solely to provide a name when executing ShowNumpadCommand. If that's the case, forget the property and just pass DisplayName as your command parameter.

public ICommand ShowNumpadCommand { get; }

public DoubleParameterViewModel(Parameter<double> parameter)
{
    this.parameter = parameter;
    ShowNumpadCommand = new RelayCommand<string>(ShowNumpad);
}

private void ShowNumpad(string parameterName)
{
    /* ... */
}

Get rid of the Style, and bind your button's command parameter to its owner's DisplayName:

<UserControl x:Name="EditorRoot">
  <!-- ... -->
  <Button x:Name="_button"
          Command="{Binding ShowNumpadCommand}"
          CommandParameter="{Binding ElementName=EditorRoot, Path=DisplayName}" />
  <!-- ... -->
</UserControl>

Upvotes: 2

Related Questions