用户控件的依赖属性不受Silverlight约束

时间:2011-09-15 04:44:21

标签: silverlight mvvm viewmodel

我创建了一个像数字更新的用户控件,如下所示

 <StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
    <TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
                Style="{StaticResource NumericUpDownTextBoxStyle}"

             KeyDown="InputTextBox_KeyDown" 
             KeyUp="InputTextBox_KeyUp"
             GotFocus="InputTextBox_GotFocus" 
             LostFocus="InputTextBox_LostFocus"  
             MouseWheel="InputTextBox_MouseWheel"
             MouseEnter="InputTextBox_MouseEnter"
             LayoutUpdated="InputTextBox_LayoutUpdated" 
             Text="{Binding Path=ControlValue, Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"/>
</StackPanel>

我已将ViewModel绑定到此控件,其中我将ControlValue属性设置为用户控件模板文本框的TextBox属性。

在控制级别,Everthing工作正常。我已从usercontrol中暴露出来。

public static readonly DependencyProperty MaximumValueProperty;

public static readonly DependencyProperty MinimumValueProperty;

public static readonly DependencyProperty StepValueProperty;

public static readonly DependencyProperty TextValueProperty;

我的属性

 public double Maximum
{
    get
    {
        return (double)GetValue(MaximumValueProperty);
    }
    set
    {
        SetValue(MaximumValueProperty, value);
        this.ViewModel.Maximum = this.Maximum;
    }
}

public double Minimum
{
    get
    {
        return (double)GetValue(MinimumValueProperty);
    }
    set
    {
        SetValue(MinimumValueProperty, value);
        this.ViewModel.Minimum = this.Minimum;
    }
}

public double Step
{
    get
    {
        return (double)GetValue(StepValueProperty);
    }
    set
    {
        SetValue(StepValueProperty, value);
        this.ViewModel.Step = this.Step;
    }
}

public double TextValue
{
    get
    {
        return (double)GetValue(TextValueProperty);
    }
    set
    {
        SetValue(TextValueProperty, value);
        this.ViewModel.ControlValue = Convert.ToString(value);
    }
}

财产的初始化。

  static NumericUpDown()
    {
        MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
        TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(null));
    }

我在MainPage.xaml页面中的Usercontrol实现如下

 <local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>

我有另一个ViewModel,它绑定到XAML页面,ViewModel中有一个属性,它绑定到Usercontrol的 TextValue 属性。

FramePrice是View模型中的属性,我绑定到用户控件的TextValue属性

和主页XAML是

<UserControl x:Class="DatePicker.MainPage"
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:local="clr-namespace:DatePicker"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel Orientation="Vertical">

        <local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>
        <Button Content="Show Date" Height="23" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
    </StackPanel>
</Grid>

我使用用户控件的页面的View模型。点击事件我向用户显示TextValue。

公共类MainPageViewModel:EntityViewModel     {

    public MainPageViewModel()
    {

    }
    private double framePrice;
    public Double FramePrice
    {
        get
        {
            return framePrice;
        }
        set
        {
            framePrice = value;
            PropertyChangedHandler("FramePrice");
        }
    }
}

当我在用户控件中更改TextValue时,它在页面视图模型的FramePrice属性中不会更改。

代码中有什么问题。???

根据 Luke Woodward 的帖子,我更新了以下代码

 public static readonly DependencyProperty MaximumValueProperty;
    public static readonly DependencyProperty MinimumValueProperty;
    public static readonly DependencyProperty StepValueProperty;
    public static readonly DependencyProperty TextValueProperty;

    public static double Max;
    public static double Min;
    public static double Stp;
    public static double Val;

  public double Maximum
    {
        get
        {
            return (double)GetValue(MaximumValueProperty);
        }
        set
        {
            SetValue(MaximumValueProperty, value);
        }
    }

    public double Minimum
    {
        get
        {
            return (double)GetValue(MinimumValueProperty);
        }
        set
        {
            SetValue(MinimumValueProperty, value);
        }
    }

    public double Step
    {
        get
        {
            return (double)GetValue(StepValueProperty);
        }
        set
        {
            SetValue(StepValueProperty, value);
        }
    }

    public double TextValue
    {
        get
        {
            return (double)GetValue(TextValueProperty);
        }
        set
        {
            SetValue(TextValueProperty, value);
        }
    }
  static NumericUpDown()
    {
        MaximumValueProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMaximumValueChanged)));
        MinimumValueProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onMinimumValueChanged)));
        StepValueProperty = DependencyProperty.Register("Step", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onStepValueChanged)));
        TextValueProperty = DependencyProperty.Register("TextValue", typeof(double), typeof(NumericUpDown), new PropertyMetadata(new PropertyChangedCallback(onTextValueChanged)));
    }

    private static void onStepValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Stp = (double)e.NewValue;
    }
    private static void onMinimumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Min = (double)e.NewValue;
    }
    private static void onMaximumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Max = (double)e.NewValue;
    }
    private static void onTextValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Val = (double)e.NewValue;
    }

然后我在用户控件的视图模型中访问 Max,Min,Stp和Val 属性来执行我的逻辑。

和XAML代码如下

<local:NumericUpDown x:Name="ctlUpDown" Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding Path=FramePrice}"></local:NumericUpDown>

和用户控制的XAML

<StackPanel Margin="5" Orientation="Horizontal" VerticalAlignment="Center">
    <TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
             Height="23" VerticalAlignment="Top" 
             Width="50" TextAlignment="Center"  
             KeyDown="InputTextBox_KeyDown" 
             KeyUp="InputTextBox_KeyUp"
             GotFocus="InputTextBox_GotFocus" 
             LostFocus="InputTextBox_LostFocus"  
             MouseWheel="InputTextBox_MouseWheel"
             MouseEnter="InputTextBox_MouseEnter"
             Text="{Binding Path=TextValue, ElementName=ctlUpDown,  Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,NotifyOnValidationError=True}"
             />
</StackPanel>

2 个答案:

答案 0 :(得分:1)

我注意到您的代码错误的第一件事是属性MaximumMinimumStepTextValue。这是TextValue属性:

public double TextValue
{
    get
    {
        return (double)GetValue(TextValueProperty);
    }
    set
    {
        SetValue(TextValueProperty, value);
        this.ViewModel.ControlValue = Convert.ToString(value);
    }
}

依赖项属性支持的属性(例如上面提到的四个属性)始终应如下所示:

public double TextValue
{
    get { return (double)GetValue(TextValueProperty); }
    set { SetValue(TextValueProperty, value); }
}

换句话说,getter只包含对GetValue的调用,而setter只包含对SetValue的调用。

原因是当Silverlight更改TextValue依赖项属性的值时,它不会通过使用上面的属性来执行此操作。依赖项属性的值存储在Silverlight依赖关系系统中,当Silverlight想要更改其中一个的值时,它会直接转到此依赖关系系统。它根本不会调用您的代码。上面提供的属性仅为方便起见,为您提供了一种访问和更改依赖项属性中存储的值的简便方法。除了您自己的代码之外,它们永远不会被其他任何东西调用。

通常,如果要在依赖项属性值更改时调用方法,则需要在注册依赖项属性时在PropertyMetadata中传递PropertyChangedCallback。但是,我怀疑在你的情况下你不需要这样做。

在我看来,你有三个属性:

  • 视图模型类中的FramePrice属性
  • NumericUpDown用户控件的TextValue依赖项属性
  • NumericUpDown用户控件的XAML中TextBox的Text依赖项属性。

我的印象是,您希望视图模型中的FramePrice属性始终具有与TextBox的Text属性相同的值。为此,您需要将FramePrice属性绑定到NumericUpDown的TextValue属性,然后将其绑定到TextBox的Text属性。

要将这两个属性绑定在一起,有几点需要改变。首先,TextValue元素中的<local:NumericUpDown>属性应该类似于

TextValue="{Binding Path=FramePrice}"

绑定{Binding ElementName=FramePrice, Path=DataContext.FramePrice}不起作用,因为XAML中没有属性x:Name="FramePrice"的元素。 ElementName{Binding ...}属性的值必须与XAML中对象的x:Name匹配。

您还需要为主页面设置DataContext。如果您的主页面视图模型对象具有零参数构造函数,则执行此操作的一种方法是遵循this answer

要将后两个属性绑定在一起,我会:

  • x:Name属性添加到NumericUpDown控件的<UserControl>元素(例如x:Name="ctlUpDown"),
  • 使用以下内容替换NumericUpDown控件中Text的{​​{1}}属性:

    TextBox

完成后,您可以从代码隐藏类中删除所有行Text="{Binding Path=TextValue, ElementName=ctlUpDown, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True}"/> 。它们不是必需的,正如我已经解释过的那样,当你想要它们时它们不会被运行。

最后,您是否有理由不使用Silverlight Toolkit的NumericUpDown控件?

编辑2 :根据我更好的判断,我看了一下你上传的两个Silverlight项目之一(我忽略了一个带有_2的项目)。它与你的问题几乎没有相似之处。

我只能假设您希望两个文本框(其中一个在用户控件中)始终具有相同的值。在做出以下更改后,我能够做到这一点:

  • MainPageViewModel.cs:将this.ViewModel.SomeProperty = ...添加到属性设置器。 (否则验证错误永远不会被清除。)

  • MyUserControlWVM.xaml:删除了对ClearErrorFromProperty("DPropertyBind");事件处理程序的引用,在Text属性上添加了绑定,并向LostFocus元素添加了添加x:Name属性。换句话说,现在看起来如下:

    <UserControl>
  • MyUserControlWVM.xaml.cs:将依赖项属性<UserControl x:Class="DependencyPropertyBinding.MyUserControlWVM" 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" mc:Ignorable="d" x:Name="ctlWVM" d:DesignHeight="300" d:DesignWidth="205"> <StackPanel Orientation="Horizontal" Width="204" Height="32"> <TextBox x:Name="textbox" Height="30" Width="200" Text="{Binding Path=DProperty, ElementName=ctlWVM, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" /> </StackPanel> </UserControl> 重命名为DependencyPropertyValue(命名约定是DPropertyProperty字段具有属性的名称(在本例中为{附加static readonly的{​​1}})。我还删除了DProperty事件处理程序。

答案 1 :(得分:0)

如果上面的代码是准确的,那么您已将 FramePrice 拼写为绑定中的 FranePrice

输出页面应该在加载页面时将其显示为绑定错误。

目前 Binding ElementName=FranePrice, Path=DataContext.FranePrice

应该是: Binding ElementName=FramePrice, Path=DataContext.FramePrice

“具有强大的绑定功能带来了很大的责任”:)