none
如何在DataTrigger的Value中设定程序级变量? UserControl中如何绑定窗体变量? RRS feed

  • 问题

  • 两个问题

    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
    2009年7月5日 9:05

答案

  • 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.
    2009年7月6日 13:39
  • 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}">
    这种写法没法实现
    2009年7月7日 10:47

全部回复

  • 原来兄弟你已经在了~~~呵呵
    wpf 我真的不熟悉


    紫柔版主的头像真叫萌得一个不行啊。。。。
    答案800 撒花
    2009年7月5日 13:08
  • heihei 看你在这回复的不少就找到你地址问了
    2009年7月5日 13:13
  • 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.
    2009年7月6日 13:39
  • 嗯 你的代码可以解决第二个问题 但是能不能直接定义为Window1的属性呢? 而不用再定义一个类
    在UserControl绑定这个属性也有些麻烦
    定义一个依赖属性与实现INotifyPropertyChanged接口 这两种方法有什么区别没有?
    还有直接将window Element作为Source 有没有性能上的影响?




    另外 那第一个问题....
    DataTrigger中   Value  该如何设定一个程序级别的变量?
    在Binding中通常容易做到  但是Value中有点麻烦
    比如一个条目列表 判断该条目作者是否当前登录用户
    可以登录也可以不登录 但是登录之后最好有反馈 即是一个依赖属性
    • 已编辑 aze 2009年7月6日 14:10
    2009年7月6日 14:03
  • 你可以在window窗体里面定义一个属性。一开始我以为你要绑定到CLR属性。 如果定义为依赖属性,则不需要去实现INotifyPropertyChanged接口,因为依赖属性的改变通知事件是固有的。

    将window element 作为source对性能应该没有影响,因为在数据绑定中,这还是常见的应用。

    对于你的第一个问题,我不知道我有没有理解错你的需求。 你有没有尝试过将Value以同样的方式绑定到程序中的property上?


    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月6日 14:55
  • 关于在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
    2009年7月6日 15:20
  • 恩,只有依赖属性才能设置绑定。对于你的情况,我有个建议。 你可以在资源里面添加一个对象,然后把Value设为这个资源。当然我们这里用DynamicResource,最终我们可以在程序中更改资源。 你可以尝试下,这一做法。

    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月6日 15:35
  • DataTrigger中Value属性也不支持DynamicResource
    ...
    “DynamicResourceExtension”不是有效的触发条件。
    ...
    2009年7月6日 15:53
  • 好的,我知道了。 不过今天有点晚了,我明天再帮你解决你这个问题吧。
    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月6日 16:00
  • OK 多谢
    2009年7月6日 16:06
  • 再看第二个问题 即在UserControl中绑定所在窗体的属性
    是可以这么实现
    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType={x:Type local:Window1}},Path=WindowUserID}" Value="">

    通过更改AncestorLevel对多层嵌套的UserControl应该也适用
    但很疑惑性能问题 除此之外别无他法?
    2009年7月7日 3:09
  • 再看第二个问题 即在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.
    2009年7月7日 3:21
  • 也就是说
    在机制上Value的值即使我把他改变了 也不会有通知 对吧?

    其实单就设定值考虑使用转换器应该可以变通实现 但是不确定会不会有反馈
    既然现在Value改变也不会有反馈的话 还是考虑用转换器? 转换器中应该是比较容易拿到变量了  Global.Instance.AppUserID


    不过还是疑惑 像这样的写法真的不能实现了?
    <DataTrigger Binding="{Binding XPath=author}" Value="{x:Static local:Global.Instance.AppUserID}">
    因为查阅看有些文章中DataTrigger的Value使用了x:Static ......

    2009年7月7日 3:51
  • 测试了下 通过转换器实现时 若更改了全局变量确实不会有反馈

    转换器中
    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";
    }
    2009年7月7日 4:34
  • 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.
    2009年7月7日 5:16
  • 测试了下 通过转换器实现时 若更改了全局变量确实不会有反馈

    转换器中


    如果你用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
    2009年7月7日 5:22
  • 目前情形下转换器是必须 但是
    ...
    不能在“Binding”类型的“Converter”属性上设置“DynamicResourceExtension”。
    ...
    2009年7月7日 5:54
  • 测试了下 通过转换器实现时 若更改了全局变量确实不会有反馈

    转换器中

    请看一下你的程序, 你绑定的数据源是XML里面的一个字段,你怎么能期盼Global变量改变时会反馈到TextBlock的Foreground属性中?转换器只是起到自定义逻辑的值转换的功能。

    我想你可以换个思路,直接在资源里面定义个变量,在用户登录后,设置这个变量。再用个触发器去做些属性设置的工作。

    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月7日 8:51
  • 因为上面小的测试例子 现在问题有点绕了...

    再确定下最初问题的情形 很平常的一个情形....

    就如同现在在浏览的这个帖子页面一样
    一个列表其中许多条目 每个条目有一个author属性
    现在要做的是判断每个条目author属性的值是不是等于当前登录用户 AppUserID
    (比如现在看到的如果是自己的帖子显示出编辑与删除)

    触发需要当前绑定的author与程序变量AppUserID


    刚才试一下 传一个ConverterParameter
    这个ConverterParameter设置为一个动态资源 存放AppUserID
    但是不可以 同样的原因 ....不能在“Binding”类型的“ConverterParameter”属性上设置“DynamicResourceExtension”。....
    • 已编辑 aze 2009年7月7日 10:00
    2009年7月7日 9:56
  • 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}">
    这种写法没法实现
    2009年7月7日 10:47
  • 我倒是没有想到使用多重数据绑定,因为在我看来Converter一般都是用来格式化数据的。 我估计你是在Converter里面去比较这两个值是否相等,然后返回bool型变量。不过在这个问题中,似乎是一个不错的技巧。

    在我的测试中,binding是不能够支持DynamicResource的。好像你自己使用的时候也出现问题的吧。
    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月7日 11:06
  • 恩,奇怪我回的一个帖子丢了。

    我没想过去用多重绑定去解决这个问题,是因为我认为转换器被设计为数据或数据类型之间的转换,或者数据的格式话,没想过去用它完成一些其它的功能。 在你的这个问题中,看起来是一个不错的技巧。


    本来就该如此简单..:).
    >有点小遗憾 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.
    2009年7月7日 11:16
  • 嗯 是的 在converter中比较是否相等
    自己定义个x:Singleton就有点麻烦了
    OK 就这样了.:).  thanks a lot
    • 已编辑 aze 2009年7月7日 11:32
    2009年7月7日 11:27
  • 呵呵,不用谢。
    Please mark the replies as answers if they help and unmark if they don't.
    2009年7月7日 11:27
  • 不能在“DataTrigger”类型的“Value”属性上设置“Binding”。只能在 DependencyObject 的 DependencyProperty 上设置“Binding”。
    2015年4月1日 10:40