绑定到模板内的整个对象时如何强制刷新绑定

发布于 2024-12-09 13:00:00 字数 3201 浏览 0 评论 0原文

我有一个“规则”类列表,它是 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
}

编辑

这是规则类的一部分:

   /// <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); 
        }
    }

I have a List of 'Rule' classes that is the source of a DataGrid. In this example I have one of the columns which is a DataGridTemplateColumn that is bound to the 'Verified' dependency property.

The problem I am having is that I have a VerifyColorConverter where I wish to pass in the ENTIRE 'Rule' object of the selected row so I can examine the Rule instance and return an appropriate brush. I do this in when setting the background of the Border (see code below - 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>

This all works well when I set the source on the DataGrid but when the underlying 'Rule' object is altered the converter is not called upon so the brush stays the same. How can I force this to be updated when I alter some of the properties of the 'Rule' instance?

Any help appreciated!

Converter looks roughly like this:

    [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
}

EDIT

This is part of Rule class:

   /// <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); 
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

那伤。 2024-12-16 13:00:00

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

将 MultiConverter

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;
}

XAML 用于 MultiConverter:

<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");
}

You could try using a MultiValueConverter instead of a regular Converter, and pass it whatever Rule Properties you need, or you can raise a CollectionChanged event when a property on the Rule gets changed. I usually prefer not to do this since I don't know how this affects performance, but it's an option.

Using a MultiConverter

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;
}

XAML for MultiConverter:

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

Using Property Change Events

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");
}
无法言说的痛 2024-12-16 13:00:00

好的 - 我找到了解决这个问题的方法 - 我所做的是将对象公开为属性,然后在任何更改时调用该属性的 OnPropertyChanged 。当属性发生变化时,绑定对象会正确拾取该值并移交给转换器!

    [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>

OK - I found a way around this - what I have done is expose the object as a property and then call the OnPropertyChanged for this property whenever anything changes. This is picked up properly by the Bind object and handed over to the Converter whenever a property changes!!

    [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; }
    }

and other classes can derived from BindElement2 and do the same: (create a property Myself that exposes the instance itself)

    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; }
    }
}

Now in the xaml I can do this: (note the Binding Path=Myself) which then ensures the entire object is send to the converter WHENEVER any property changes!!

<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>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文