【问题标题】:How to force a refresh of the binding when bound to the entire object within a Template绑定到模板中的整个对象时如何强制刷新绑定
【发布时间】:2011-10-11 15:51:39
【问题描述】:

我有一个“规则”类列表,它是 DataGrid 的来源。在此示例中,我有一列是绑定到“已验证”依赖项属性的 DataGridTemplateColumn。

我遇到的问题是我有一个 VerifyColorConverter,我希望在其中传递所选行的整个“规则”对象,以便检查规则实例并返回适当的画笔。我在设置边框的背景时这样做(参见下面的代码 - Background="{Binding Converter={StaticResource convVerify}}"

<DataGridTemplateColumn Header="Verified" Width="150">
<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Border Background="{Binding Converter={StaticResource convVerify}}" 
                CornerRadius="4" Height="17" Margin="2,0,2,0" VerticalAlignment="Center" >
            <Grid>
                <TextBlock Foreground="Yellow" Text="{Binding Path=Verified, Mode=OneWay}" TextAlignment="Center" VerticalAlignment="Center" 
                        FontSize="11" FontWeight="Bold" />
            </Grid>
        </Border>
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

当我在 DataGrid 上设置源时,这一切都很好,但是当底层的“规则”对象被改变时,转换器不会被调用,所以画笔保持不变。当我更改“规则”实例的某些属性时,如何强制更新它?

任何帮助表示赞赏!

转换器大致如下:

    [ValueConversion(typeof(CRule), typeof(SolidColorBrush))]
public class VerifyColorConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        CRule rule = value as CRule;

        Color clr = Colors.Red;

        int count = 0;
        int verified = 0;

        if (rule != null)
        {
            count = rule.TotalCount;    
            verified = rule.NoOfVerified; 
        }

        if (count == 0) clr = Colors.Transparent;
        else if (verified == 0) clr = (Color)ColorConverter.ConvertFromString("#FFD12626");
        else if (verified < count) clr = (Color)ColorConverter.ConvertFromString("#FF905132");
        else clr = (Color)ColorConverter.ConvertFromString("#FF568D3F");

        SolidColorBrush brush = new SolidColorBrush(clr);
        return brush;
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

编辑

这是 Rule 类的一部分:

   /// <summary>
/// Compliance Restriction (Rule)
/// </summary>
public class Rule : BindElement
{
    public CMode Mode { get; private set; }
    public int RuleID { get; private set; }
    public string RuleDescription { get; private set; }

    private int _NoOfVerified = 0;
    private int _TotalCount = 0;

    public int NoOfVerified
    {
        get { return _NoOfVerified; }
        set { _NoOfVerified = value; RaiseChanged("Progress"); RaiseChanged("Verified"); }
    }

    public int TotalCount
    {
        get { return _TotalCount; }
        set { _TotalCount = value; RaiseChanged("Progress"); RaiseChanged("Verified"); }
    }

    public string Verified 
    {
        get 
        {
            if (TotalCount == 0) return "Nothing to verify";
            return string.Format("Verified {0} out of {1}", NoOfVerified, TotalCount); 
        }
    }

【问题讨论】:

    标签: wpf wpfdatagrid


    【解决方案1】:

    您可以尝试使用MultiValueConverter 而不是常规的Converter,并将您需要的任何规则属性传递给它,或者您可以在规则上的属性发生更改时引发CollectionChanged 事件。我通常不喜欢这样做,因为我不知道这会如何影响性能,但这是一种选择。

    使用多转换器

    public object Convert(object[] values, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        Color clr = Colors.Red;
    
        int count = 0;
        int verified = 0;
    
        if (values.Count >= 2
            && int.TryParse(count, values[0].ToString()) 
            && int.TryParse(verfieid, values[1].ToString()))
        {
            if (count == 0) clr = Colors.Transparent;
            else if (verified == 0) clr = (Color)ColorConverter.ConvertFromString("#FFD12626");
            else if (verified < count) clr = (Color)ColorConverter.ConvertFromString("#FF905132");
            else clr = (Color)ColorConverter.ConvertFromString("#FF568D3F");
        }
    
        SolidColorBrush brush = new SolidColorBrush(clr);
        return brush;
    }
    

    用于 MultiConverter 的 XAML:

    <Border.Background>
        <MultiBinding Converter="{StaticResource convVerify}">
            <Binding Value="{Binding TotalCount}" />
            <Binding Value="{Binding NoOfVerified}" />
        </MultiBinding>
    </Border.Background>
    

    使用属性更改事件

    RulesCollection.CollectionChanged += RulesCollection_Changed;
    
    void RulesCollection_Changed(object sender, CollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            foreach(Rule rule in e.NewItems) // May need a cast here
                rule.PropertyChanged += Rule_PropertyChanged;
    
        if (e.OldItems != null)
            foreach(Rule rule in e.OldItems) // May need a cast here
                rule.PropertyChanged -= Rule_PropertyChanged;
    }
    
    void Rule_PropertyChanged()
    {
        RaisePropertyChanged("RulesCollection");
    }
    

    【讨论】:

    • 我不能这样做,因为它是 DataGridTemplateColumn 模板的一部分,而不是我周围的 Rule 对象。我为网格提供了一个规则对象列表,我希望将规则传递给转换器,而不是像在其他列中那样定义路径(或者我在示例代码中显示的那个 TextBlock)。
    • 我想我明白你现在在说什么了——我误解了这个问题。在您的 ViewModel 中,为什么不向每个 Rule 添加一个 PropertyChange 事件,以便在任何参数更改时引发整个规则的 OnPropertyChanged 事件?
    • 感谢您到目前为止的帮助 Rachel - 我已经更新了代码以显示 Rule 类的一部分。我明白您的评论是什么意思,但是如果我将 List 放置到网格的来源,我将如何处理?我如何“踢”那个特定的规则?
    • 没有意识到您正在绑定到 List。尝试将List&lt;Rule&gt; 替换为ObservableCollection&lt;Rule&gt;。默认情况下,List 的属性发生更改时不会通知 UI,而 ObservableCollection 会。
    • 对不起 - 我的错 - 我绑定到一个可观察的集合 - 我说错了......(这是我的行:gridRules.ItemsSource = _ocRules;)
    【解决方案2】:

    好的 - 我找到了解决这个问题的方法 - 我所做的是将对象公开为属性,然后在任何更改时为此属性调用 OnPropertyChanged。这会被 Bind 对象正确拾取,并在属性更改时移交给 Converter!

        [Serializable()]
    public class BindElement : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
    
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }
    
        public void RaiseChanged(string s)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(s));
        }
    
        #endregion
    }
    
    public class BindElement2 : BindElement
    {
        public void RaiseChanged(string s)
        {
            base.RaiseChanged(s);
            NudgeMyself();
        }
    
        public void NudgeMyself()
        {
            base.RaiseChanged("Myself");
        }
    }
    
       /// <summary>
    /// Compliance Restriction (Rule)
    /// </summary>
    public class CRule : BindElement2
    {
        public CMode Mode { get; private set; }
        public int RuleID { get; private set; }
        public string RuleDescription { get; private set; }
    
        private int _NoOfVerified = 0;
        private int _TotalCount = 0;
    
        public int NoOfVerified
        {
            get { return _NoOfVerified; }
            set { _NoOfVerified = value; RaiseChanged("Progress"); RaiseChanged("Verified"); }
        }
    
        public int TotalCount
        {
            get { return _TotalCount; }
            set { _TotalCount = value; RaiseChanged("Progress"); RaiseChanged("Verified"); }
        }
    
        public string Verified 
        {
            get 
            {
                if (TotalCount == 0) return "Nothing to verify";
                return string.Format("Verified {0} out of {1}", NoOfVerified, TotalCount); 
            }
        }
    
        public CRule Myself
        {
            get { return this; }
        }
    

    其他类可以从 BindElement2 派生并做同样的事情:(创建一个属性 Myself 来公开实例本身)

        public class CTradeRule : BindElement2
    {
        public CRule Rule { get; set; }
        public bool IsLocked { get; set; }      // if true this should prevent a Reason from being given
        public bool IsVerified { get { return Reason.Length > 0; } }
    
        private string _Reason = "";        // ** no reason **
        public string Reason 
        {
            get { return _Reason; }
            set { _Reason = value; RaiseChanged("Reason"); }
        }
    
        public int Progress
        {
            get { return (IsVerified ? 1 : 0); }
        }
    
        public override string ToString()
        {
            return string.Format("Rule: {0}, Reason: {1}", Rule.RuleID, _Reason);
        }
    
        public CTradeRule Myself
        {
            get { return this; }
        }
    }
    

    现在在 xaml 中我可以这样做:(注意 Binding Path=Myself)然后确保在任何属性更改时将整个对象发送到转换器!

    <DataGridTemplateColumn Header="Verified" Width="150">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Border Background="{Binding Path=Myself, Converter={StaticResource convVerify}}" 
                    CornerRadius="4" Height="17" Margin="2,0,2,0" VerticalAlignment="Center" >
                <Grid>
                    <TextBlock Foreground="Yellow" Text="{Binding Path=Verified, Mode=OneWay}" TextAlignment="Center" VerticalAlignment="Center" 
                            FontSize="11" FontWeight="Bold" />
                </Grid>
            </Border>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    

    【讨论】:

      猜你喜欢
      • 2011-08-06
      • 1970-01-01
      • 2019-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      相关资源
      最近更新 更多