积极答复者
如何在DataTrigger的Value中设定程序级变量? UserControl中如何绑定窗体变量?

问题
-
两个问题
DataTrigger中 Value该如何设定一个程序级别的变量
比如一个条目列表 判断该条目作者是否当前登录用户
可以登录也可以不登录 但是登录之后最好有反馈 即是一个依赖属性
另外就是如何绑定窗体的变量?
绑定的位置在UserControl中
比如一个
Window1.xaml
其上放置有UserControl1 UserControl2
<Window x:Class="WpfApplication5.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication5" Title="Window1" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition Height="*" /> <RowDefinition Height="70" /> </Grid.RowDefinitions> <TextBox Visibility="Collapsed" HorizontalAlignment="Left" Name="textBox1" Width="120" Height="25" /> <local:UserControl1 HorizontalAlignment="Left" Grid.Row="1" Background="AntiqueWhite" x:Name="uc1"/> <local:UserControl2 HorizontalAlignment="Left" Grid.Row="2" Background="AliceBlue" x:Name="uc2" /> </Grid> </Window>
后面定义了
public partial class Window1 : Window { public static readonly DependencyProperty WindowUserIDProperty = DependencyProperty.Register("WindowUserID", typeof(string), typeof(Window1), new PropertyMetadata("")); public string WindowUserID { get { return (string)GetValue(WindowUserIDProperty); } set { SetValue(WindowUserIDProperty, value); } } public Window1() { InitializeComponent(); } }
现在我在uc1中改变
private void button1_Click(object sender, RoutedEventArgs e) { (Window.GetWindow(this) as Window1).WindowUserID = textBox1.Text; }
希望在uc2中反映出来
<UserControl x:Class="WpfApplication5.UserControl2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="50" Width="200"> <Grid> <Grid.Resources> <Style x:Key="lblFlag" TargetType="Label"> <Setter Property="Background" Value="AntiqueWhite"/> <Style.Triggers> <DataTrigger Binding="{Binding Source=???,Path=WindowUserID}" Value=""> <Setter Property="Content" Value="UnSigned" /> </DataTrigger> </Style.Triggers> </Style> </Grid.Resources> <Label Height="28" Style="{StaticResource lblFlag}" HorizontalAlignment="Left" Margin="10,10,0,0" Name="label1" VerticalAlignment="Top" Width="120">Singed</Label> </Grid> </UserControl>
另外有时触发器与转换器可以达到同样效果 从性能上来说 哪个更推荐呢
- 已编辑 aze 2009年7月6日 10:17
答案
-
aze 你好:
根据你的应用场景, 我做了一个例子将datatrigger 中的Value绑定到Window里面的一个自定义的对象Test。Author属性。 如果Author的值为abc,那么将button的IsEanble属性设为False。代码如下:
C# 代码:
/// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { Test t = new Test(); public Test T { get { return t; } set { t = value; } } public Window1() { t.Author = "abc"; InitializeComponent(); } } public class Test : INotifyPropertyChanged { private string author; public event PropertyChangedEventHandler PropertyChanged; public string Author { get { return author; } set { if (value != author) { author = value; OnPropertyChanged("Author"); } } } protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } }
XAML 代码:
<Window x:Class="StoaryBoardViaCode.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:tst="clrnamespace:StoaryBoardViaCode" Name="window1" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <Style x:Key="btnStyle" TargetType="{x:Type Button}"> <Style.Triggers> <DataTrigger Binding="{Binding ElementName=window1,Path = T.Author}" Value="abc"> <Setter Property="IsEnabled" Value="false"> </Setter> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <Button Style="{StaticResource btnStyle}">dddd</Button> </StackPanel> </Window>
我想你应该能够从这个代码示例中获取你想要的功能,如果我理解错了你的需求,或者你不能够根据示例实现你的最终需求,请告诉我。
Regards,
Bruce
Please mark the replies as answers if they help and unmark if they don't.- 已标记为答案 Jim Zhou - MSFTModerator 2009年7月7日 11:49
-
OK了 OK了 使用 MultiBinding
<DataTrigger> <DataTrigger.Binding> <MultiBinding Converter="{StaticResource isAppUserConverter}"> <Binding XPath="@author"/> <Binding Path="AppUserID" Source="{x:Static local:Global.Instance}"/> </MultiBinding> </DataTrigger.Binding> <DataTrigger.Value>true</DataTrigger.Value> <Setter Property="Background" Value="Cyan" /> </DataTrigger>
cs中也仅仅直接更改AppUserID即可
private void button1_Click(object sender, RoutedEventArgs e) { Global.Instance.AppUserID = "aze"; }
本来就该如此简单..:).
有点小遗憾 Binding中能不能绑定一个DynamicResource?
那样的话就不用我多定义一个Singleton的Global类来置放这个AppUserID了
还有耿耿于怀的一点
<DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
这种写法没法实现- 已标记为答案 Jim Zhou - MSFTModerator 2009年7月7日 11:49
全部回复
-
aze 你好:
根据你的应用场景, 我做了一个例子将datatrigger 中的Value绑定到Window里面的一个自定义的对象Test。Author属性。 如果Author的值为abc,那么将button的IsEanble属性设为False。代码如下:
C# 代码:
/// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { Test t = new Test(); public Test T { get { return t; } set { t = value; } } public Window1() { t.Author = "abc"; InitializeComponent(); } } public class Test : INotifyPropertyChanged { private string author; public event PropertyChangedEventHandler PropertyChanged; public string Author { get { return author; } set { if (value != author) { author = value; OnPropertyChanged("Author"); } } } protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } }
XAML 代码:
<Window x:Class="StoaryBoardViaCode.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:tst="clrnamespace:StoaryBoardViaCode" Name="window1" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <Style x:Key="btnStyle" TargetType="{x:Type Button}"> <Style.Triggers> <DataTrigger Binding="{Binding ElementName=window1,Path = T.Author}" Value="abc"> <Setter Property="IsEnabled" Value="false"> </Setter> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <Button Style="{StaticResource btnStyle}">dddd</Button> </StackPanel> </Window>
我想你应该能够从这个代码示例中获取你想要的功能,如果我理解错了你的需求,或者你不能够根据示例实现你的最终需求,请告诉我。
Regards,
Bruce
Please mark the replies as answers if they help and unmark if they don't.- 已标记为答案 Jim Zhou - MSFTModerator 2009年7月7日 11:49
-
嗯 你的代码可以解决第二个问题 但是能不能直接定义为Window1的属性呢? 而不用再定义一个类
在UserControl绑定这个属性也有些麻烦
定义一个依赖属性与实现INotifyPropertyChanged接口 这两种方法有什么区别没有?
还有直接将window Element作为Source 有没有性能上的影响?
另外 那第一个问题....
DataTrigger中 Value 该如何设定一个程序级别的变量?
在Binding中通常容易做到 但是Value中有点麻烦
比如一个条目列表 判断该条目作者是否当前登录用户
可以登录也可以不登录 但是登录之后最好有反馈 即是一个依赖属性- 已编辑 aze 2009年7月6日 14:10
-
你可以在window窗体里面定义一个属性。一开始我以为你要绑定到CLR属性。 如果定义为依赖属性,则不需要去实现INotifyPropertyChanged接口,因为依赖属性的改变通知事件是固有的。
将window element 作为source对性能应该没有影响,因为在数据绑定中,这还是常见的应用。
对于你的第一个问题,我不知道我有没有理解错你的需求。 你有没有尝试过将Value以同样的方式绑定到程序中的property上?
Please mark the replies as answers if they help and unmark if they don't. -
关于在DataTrigger中设置Value的值提示是不能使用Binding的
......
不能在“DataTrigger”类型的“Value”属性上设置“Binding”。
只能在 DependencyObject 的 DependencyProperty 上设置“Binding”
......
我做了一个Singleton 的类放置全局程序级变量
将其绑定到DataTrigger的Binding中是可以的 但如何设置到Value中就不清楚了
如下 在Binding表达式中可以这种形式 没有问题 (但这不能实现我的功能 不是我想要的)
<DataTrigger Binding="{Binding Source={x:Static local:Global.Instance},Path=AppUserID}" Value="aze">
问题就是该怎么放于Value中
情景期望是这种形式 但实际上有错的
<DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
AppUserID提供更改通知
public class Global : System.ComponentModel.INotifyPropertyChanged { private static readonly Global instance = new Global(); public static Global Instance { get{ return instance;} } private Global() { } string appUserID = ""; public string AppUserID { get { return appUserID; } set { appUserID = value; NotifyPropertyChange("AppUserID"); } } #region INotifyPropertyChanged 成员 public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; void NotifyPropertyChange(string proper) { if (PropertyChanged == null) return; PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(proper)); } #endregion }
- 已编辑 aze 2009年7月6日 15:28
-
再看第二个问题 即在UserControl中绑定所在窗体的属性
是可以这么实现
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType={x:Type local:Window1}},Path=WindowUserID}" Value="">
通过更改AncestorLevel对多层嵌套的UserControl应该也适用
但很疑惑性能问题 除此之外别无他法?
其实我觉得如上代码除了 写法上有些冗长,事实上性能不应该会有损失。 该操作也就是遍历树,找到符合条件的祖先节点。 其实我们也可以通过代码遍历逻辑树,实现同样的功能。
对于你的第一个问题,Value 因为不是依赖属性,所以不支持绑定和应用DynamicResource。 我想,既然这样的话,还不如使用C#过程代码去实现相同的功能,这样兴许还简单一点。
Please mark the replies as answers if they help and unmark if they don't. -
也就是说
在机制上Value的值即使我把他改变了 也不会有通知 对吧?
其实单就设定值考虑使用转换器应该可以变通实现 但是不确定会不会有反馈
既然现在Value改变也不会有反馈的话 还是考虑用转换器? 转换器中应该是比较容易拿到变量了 Global.Instance.AppUserID
不过还是疑惑 像这样的写法真的不能实现了?
<DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
因为查阅看有些文章中DataTrigger的Value使用了x:Static ......
-
测试了下 通过转换器实现时 若更改了全局变量确实不会有反馈
转换器中
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string color = "Red"; if (value.ToString() == Global.Instance.AppUserID) color = "Green"; return color; }
xaml中
<DataTemplate> <TextBlock FontSize="12" Foreground="{Binding XPath=@Stock,Converter={StaticResource appUserConverter}}" Text="{Binding XPath=Title}"/> </DataTemplate>
xaml.cs中
private void button1_Click(object sender, RoutedEventArgs e) { Global.Instance.AppUserID = "in"; }
-
Value 中是可以 使用x:Static 扩展标记的,只是你可能用了单件模式,wpf 可能不支持local:Global.Instance.AppUserID这样访问,它会将Global.Instance当着一个类型,但是又找不到这个类型。所以设计器报错。 如果你使用以下代码,是没有问题的。
<DataTrigger Binding="{Binding ElementName=window1,Path = T.Author}" Value="{x:Static local:Test.AAA}"> <Setter Property="IsEnabled" Value="false"> </Setter> </DataTrigger>
Test 类型 如下定义:
public class Test : INotifyPropertyChanged { public static string AAA = "sdfds"; }
Please mark the replies as answers if they help and unmark if they don't. -
测试了下 通过转换器实现时 若更改了全局变量确实不会有反馈
转换器中
如果你用StaticResource的话,那么后续资源的改变 不会再去设置这个Foreground 属性,你应该尝试去使用 DynamicResource.
Please mark the replies as answers if they help and unmark if they don't.- 已编辑 Bruce.Zhou 2009年7月7日 5:23 format
-
因为上面小的测试例子 现在问题有点绕了...
再确定下最初问题的情形 很平常的一个情形....
就如同现在在浏览的这个帖子页面一样
一个列表其中许多条目 每个条目有一个author属性
现在要做的是判断每个条目author属性的值是不是等于当前登录用户 AppUserID
(比如现在看到的如果是自己的帖子显示出编辑与删除)
触发需要当前绑定的author与程序变量AppUserID
刚才试一下 传一个ConverterParameter
这个ConverterParameter设置为一个动态资源 存放AppUserID
但是不可以 同样的原因 ....不能在“Binding”类型的“ConverterParameter”属性上设置“DynamicResourceExtension”。....- 已编辑 aze 2009年7月7日 10:00
-
OK了 OK了 使用 MultiBinding
<DataTrigger> <DataTrigger.Binding> <MultiBinding Converter="{StaticResource isAppUserConverter}"> <Binding XPath="@author"/> <Binding Path="AppUserID" Source="{x:Static local:Global.Instance}"/> </MultiBinding> </DataTrigger.Binding> <DataTrigger.Value>true</DataTrigger.Value> <Setter Property="Background" Value="Cyan" /> </DataTrigger>
cs中也仅仅直接更改AppUserID即可
private void button1_Click(object sender, RoutedEventArgs e) { Global.Instance.AppUserID = "aze"; }
本来就该如此简单..:).
有点小遗憾 Binding中能不能绑定一个DynamicResource?
那样的话就不用我多定义一个Singleton的Global类来置放这个AppUserID了
还有耿耿于怀的一点
<DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
这种写法没法实现- 已标记为答案 Jim Zhou - MSFTModerator 2009年7月7日 11:49
-
恩,奇怪我回的一个帖子丢了。
我没想过去用多重绑定去解决这个问题,是因为我认为转换器被设计为数据或数据类型之间的转换,或者数据的格式话,没想过去用它完成一些其它的功能。 在你的这个问题中,看起来是一个不错的技巧。
本来就该如此简单..:).
>有点小遗憾 Binding中能不能绑定一个DynamicResource?
>那样的话就不用我多定义一个Singleton的Global类来置放这个AppUserID了
在我的测试中,绑定是不能引用DynamicResource的。 你自己在测试中也遇到了同样的问题。
>还有耿耿于怀的一点
><DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
>这种写法没法实现
其实 直接绑定一个静态属性还是可以的。 如果要Value支持 上面的情况的话,你可以去自定义一个markup扩展。从头开始写比较麻烦,你可以用Reflector看下StaticExtension的源代码,然后可以利用里面的一部分Source。
然后我们就可以使用类似以下的方式去写上面的代码:
<DataTrigger Binding="{Binding XPath=author}" Value="x:Singleton Instance={x:Static Global.Instance}, Path ="UserAppID" }">
当然以上代码是完全假设你在完成自己的扩展标记类的实现后。
Please mark the replies as answers if they help and unmark if they don't.