用户控件的依赖属性在 Silverlight 中未绑定
我创建了一个像数字 updown 这样的用户控件,如下所示,
<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 属性。
一切都在控制级别上运行良好。我已经从用户控件中暴露了。
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>
,其中我有另一个绑定到 XAML 页面的 ViewModel,并且 ViewModel 中有一个属性,我绑定到 TextValue > 用户控件的属性。
FramePrice 是视图模型中的属性,我将其绑定到用户控件的 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>
我使用用户控件的页面的视图模型。单击事件时,我向用户显示 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>
I have created a user control like numeric updown as follows
<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>
I have bind a ViewModel to this control where I Set ControlValue property to TextBox property of the user control template textbox.
Everthing works fine at a control level. I have exposed from usercontrol.
public static readonly DependencyProperty MaximumValueProperty;
public static readonly DependencyProperty MinimumValueProperty;
public static readonly DependencyProperty StepValueProperty;
public static readonly DependencyProperty TextValueProperty;
My Properties are
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);
}
}
Initialization of the property.
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));
}
My Usercontrol implementation in the MainPage.xaml page as follows
<local:NumericUpDown Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding ElementName=FranePrice, Path=DataContext.FranePrice}"></local:NumericUpDown>
Where I have another ViewModel which i bind to the XAML page and there is a Property in the ViewModel which i bind to the TextValue property of the Usercontrol.
FramePrice is property in the View model that i bind to the TextValue property of the user control
and Main page XAML is
<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>
This View model of the page where i used user control. On click event i showing TextValue to user.
public class MainPageViewModel : EntityViewModel
{
public MainPageViewModel()
{
}
private double framePrice;
public Double FramePrice
{
get
{
return framePrice;
}
set
{
framePrice = value;
PropertyChangedHandler("FramePrice");
}
}
}
When I change the TextValue in the User control it doesnot change in the FramePrice property of the page viewmodel.
Is anything wrong in the code.???
As per Luke Woodward's post I have updated code as follows
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;
}
Then i accessed Max, Min , Stp and Val property in user control's view model to perform my logic.
and XAML code is follows
<local:NumericUpDown x:Name="ctlUpDown" Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding Path=FramePrice}"></local:NumericUpDown>
and XAML of user control
<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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我注意到您的代码错误的第一件事是属性
Maximum
、Minimum
、Step
和TextValue
。这是TextValue
属性:由依赖属性支持的属性(例如我上面提到的四个)应该ALWAYS 如下所示:
换句话说,getter 应该只包含对 GetValue 的调用,并且 setter 只应包含对 SetValue 的调用。
原因是当 Silverlight 更改 TextValue 依赖属性的值时,它不会使用上面的属性来执行此操作。依赖属性的值存储在 Silverlight 依赖系统中,当 Silverlight 想要更改其中之一的值时,它会直接访问该依赖系统。它根本不调用您的代码。提供上述属性只是为了您的方便,为您提供了一种简单的方法来访问和更改存储在依赖项属性中的值。除了您自己的代码之外,它们永远不会被任何其他东西调用。
通常,如果您希望在依赖属性值发生更改时调用某个方法,则需要在注册依赖属性时在 PropertyMetadata 中传递 PropertyChangedCallback。但是,我怀疑在您的情况下您不需要这样做。
在我看来,您有三个属性:
我的印象是,您希望视图模型中的 FramePrice 属性始终具有与 TextBox 的 Text 属性相同的值。为此,您需要将 FramePrice 属性绑定到 NumericUpDown 的 TextValue 属性,然后将其绑定到 TextBox 的 Text 属性。
要将前两个属性绑定在一起,需要更改一些内容。首先,
元素中的TextValue
属性应类似于绑定
{Binding ElementName=FramePrice, Path=DataContext.FramePrice} 不起作用,因为 XAML 中没有具有属性
x:Name="FramePrice"
的元素。{Binding ...}
中的ElementName
属性的值必须与 XAML 中对象的x:Name
相匹配。您还需要为主页设置 DataContext。如果您的主页视图模型对象具有零参数构造函数,则执行此操作的一种方法是遵循 这个答案。
要将后两个属性绑定在一起,我将:
x:Name
属性添加到 NumericUpDown 控件的
元素 (x:Name=例如,“ctlUpDown”
),将 NumericUpDown 控件中
TextBox
的Text
属性替换为以下内容:完成此操作后,您可以删除所有来自代码隐藏类的
this.ViewModel.SomeProperty = ...
行。它们不是必需的,正如我已经解释过的,它们不会在您想要的时候运行。最后,您是否有理由不使用 Silverlight Toolkit 的 NumericUpDown 控件?
编辑 2:根据我更好的判断,我查看了您上传的两个 Silverlight 项目之一(我忽略了其中包含 _2 的项目)。它与你的问题几乎没有相似之处。
我只能假设您希望两个文本框(其中之一位于用户控件中)始终具有相同的值。在进行以下更改后,我能够执行此操作:
MainPageViewModel.cs:将
ClearErrorFromProperty("DPropertyBind");
添加到属性设置器。 (否则验证错误永远不会被清除。)MyUserControlWVM.xaml:删除了对
LostFocus
事件处理程序的引用,添加了对 Text 属性的绑定,并添加了x:Name
属性到
元素。换句话说,现在看起来如下所示:MyUserControlWVM.xaml.cs:将依赖属性
DependencyPropertyValue
重命名为DPropertyProperty
(命名约定是static readonly
字段具有属性名称(在本例中为DProperty
),并附加了Property
)。我还删除了TextBox_LostFocus
事件处理程序。The first thing I noticed wrong about your code was the properties
Maximum
,Minimum
,Step
andTextValue
. Here's theTextValue
property:Properties that are backed by a dependency property, such as the four I mentioned above, should ALWAYS look like the following:
In other words, the getter should contain nothing more than a call to GetValue, and the setter should contain nothing more than a call to SetValue.
The reason for this is that when Silverlight changes the value of the TextValue dependency property, it won't do it by using the property above. The values of dependency properties are stored within the Silverlight dependency system, and when Silverlight wants to change the value of one of them, it goes directly to this dependency system. It doesn't call your code at all. Properties like that above are provided only for your convenience, giving you an easy way to access and change the value stored in the dependency property. They will never be called by anything other than your own code.
Generally, if you want a method to be called whenever a dependency property value changes, you need to pass a PropertyChangedCallback in the PropertyMetadata when registering the dependency property. However, I suspect that in your case you won't need to do that.
It seems to me that you have three properties:
My impression is that you want the FramePrice property in your view-model to always have the same value as the Text property of the TextBox. To do that, you need to bind the FramePrice property to the NumericUpDown's TextValue property, and then bind that to the Text property of the TextBox.
To bind the first two of these properties together, there are a couple of things to change. Firstly, the
TextValue
property in your<local:NumericUpDown>
element should look likeThe binding
{Binding ElementName=FramePrice, Path=DataContext.FramePrice}
won't work, because there's no element in your XAML with the attributex:Name="FramePrice"
. The value of anElementName
property in a{Binding ...}
must match thex:Name
of an object in the XAML.You also need to set up the DataContext for your main page. If your main page view-model object has a zero-argument constructor, one way of doing this is to follow this answer.
To bind the second two properties together, I would:
x:Name
attribute to the<UserControl>
element of your NumericUpDown control (x:Name="ctlUpDown"
, say),replace the
Text
property of theTextBox
within your NumericUpDown control with the following:Once you've done that, you can then remove all of the lines
this.ViewModel.SomeProperty = ...
from your code-behind class. They're not necessary, and as I've already explained they won't be run when you wanted them to.Finally, is there a reason you're not using the Silverlight Toolkit's NumericUpDown control?
EDIT 2: Against my better judgement I took a look at one of the two Silverlight projects you uploaded (I ignored the one with _2 in it). It bears very little resemblance to your question.
I can only assume you want the two textboxes (one of which is in a user control) to always have the same value. I was able to do this after making the following changes:
MainPageViewModel.cs: add
ClearErrorFromProperty("DPropertyBind");
to the property setter. (Otherwise the validation error never gets cleared.)MyUserControlWVM.xaml: removed reference to
LostFocus
event handler, added binding on Text property and added addx:Name
attribute to the<UserControl>
element. In other words, it now looks like the following:MyUserControlWVM.xaml.cs: renamed dependency property
DependencyPropertyValue
toDPropertyProperty
(the naming convention is that thestatic readonly
field has the name of the property (in this caseDProperty
) withProperty
appended). I also removed theTextBox_LostFocus
event handler.如果上面的代码准确,您已在绑定中将 FramePrice 拼写为 FranPrice
加载页面时,输出窗口应将其显示为绑定错误。
目前是
Binding ElementName=FranPrice, Path=DataContext.FanePrice
应该是:
Binding ElementName=FramePrice, Path=DataContext.FramePrice
“强大的绑定能力意味着巨大的责任” :)
If the code above is accurate you have spelt FramePrice as FranePrice in the binding
The output window should have shown this as a binding error when the page loaded.
it is currently
Binding ElementName=FranePrice, Path=DataContext.FranePrice
should be:
Binding ElementName=FramePrice, Path=DataContext.FramePrice
"With great binding capabilities comes great responsibility" :)