Chengting
Chengting

Reputation: 355

Bind ViewModel's property to UserControl's child control and itself DependencyProperty

What I want to do is to bind my usercontrol's label to a property value in ViewModel. But I also want to get notified when the label changed ( and to do other work like extract the label's new value to modify grid width and so on).

How to do this?

what I did is:

Have a viewmodel with an Voltage property, which is what I want to display.

UnitVm.cs

    private int m_V;
    public int VoltInVm
    {
        get
        { return m_V;  }
        set
        {
            if (m_V != value)
            {        
                Set<int>(ref m_V, value, nameof(Volt)); 
            }
        }
    }

and my usercontrol: Unit.cs

public partial class Unit : UserControl
{
    public static readonly DependencyProperty VoltProperty =
                      DependencyProperty.Register("Volt", typeof(int), typeof(Unit),  
                    new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((Unit)o).OnVoltChanged(o, e)));

    private void OnVoltChanged(double dVolt)
    {
            double dWidth;
            if (double.TryParse(strVal, out dWidth))
            {
                dWidth = dVolt / 380 * 100;

                if (dWidth > 100)
                    dWidth = 100;

                gridVolt.ColumnDefinitions[0].Width = new GridLength(dWidth, GridUnitType.Star);
                gridVolt.ColumnDefinitions[1].Width = new GridLength(100 - dWidth, GridUnitType.Star);
            }           
    }
    public int Volt
    {
        get { return (int)GetValue(VoltProperty); }
        set
        {
            SetValue(VoltProperty, value);
        }
    }

the DependencyProperty of VoltProperty is defined, and the work I want to do is written inside OnVoltChanged.

I mean when accepting change from ViewModel, I can call OnVoltChanged.

To use the usercontrol of Unit in a main window:

    <DockPanel DataContext="{Binding UnitVm, Source={StaticResource Locator}}">
        <Viewbox 
            <Label x:Name="lblVolt" Content="{Binding VoltInVm}" />
        </Viewbox>
    </DockPanel>

lblVolt binding to UnitVm context can update with new voltage values correctly. But how to bind to DependencyProperty of Volt in Unit? And is this the right way?

Thanks in advance.

Ting

Upvotes: 0

Views: 337

Answers (1)

Clemens
Clemens

Reputation: 128042

The view model property setter is not implemented correctly.

It should look like shown below, i.e. use the correct property name nameof(VoltInVm) and probably not check m_V != value before calling Set, because that is already done by the Set method.

private int voltInVm;

public int VoltInVm
{
    get { return voltInVm;  }
    set { Set<int>(ref voltInVm, value, nameof(VoltInVm)); }
}

When you now bind the UserControl's property like

<local:Unit Volt="{Binding VoltInVm}"/>

the PropertyChangedCallback of the Volt dependency property will be called each time the VoltInVm property changes its value.

It is however unclear what (o, e) => ((Unit)o).OnVoltChanged(o, e) in the dependency property registration is supposed to be. It should certainly look like this:

(o, e) => ((Unit)o).OnVoltChanged((int)e.NewValue)

and the method should be declared with an int argument instead of double:

private void OnVoltChanged(int volt) ...

Or - certainly better - change all voltage property types to double, in the control and in the view model.

Upvotes: 1

Related Questions