使用“仅获取”静态属性作为绑定源

本文关键字:属性 绑定 静态 获取 仅获取 使用 | 更新日期: 2023-09-27 18:25:45

>我有下面提到的类结构。

 //On the design aspects, I know It may not be the advisable approach, 
 //but something of this kind is only required.
/// <summary>
/// Paper Class
/// </summary>
public class Paper
{
    public string PaperName { get; set; }
    public bool IsPending { get; set; }
}
/// <summary>
/// PaperChecking class, Individual papers will be processed here.
/// </summary>
public class PaperChecking
{
    public static List<Paper> ListPapers { get; set; }
    public static void AddPapers()
    {
        ListPapers = new List<Paper>();
        ListPapers.Add(new Paper() { PaperName = "Paper1", IsPending = false });
        ListPapers.Add(new Paper() { PaperName = "Paper2", IsPending = false });
        ListPapers.Add(new Paper() { PaperName = "Paper3", IsPending = false });
        ListPapers.Add(new Paper() { PaperName = "Paper4", IsPending = false });
        ListPapers.Add(new Paper() { PaperName = "Paper5", IsPending = false });
    }
    public static bool IsCheckingPending
    {
        get
        {
            //List has items and it is not null, so intentionally removed the checks.
            return ListPapers.Count(paper => paper.IsPending == true) > 0 ? true : false;
        }
    }
}
/// <summary>
/// This class will select papers for processing
/// </summary>
public class ChangePaperSetting
{
    public void SelectPaper(string paperName)
    {
        //It can be assumed that Paper object will never be NULL
        PaperChecking.ListPapers.FirstOrDefault(paper => paper.PaperName.Equals(paperName)).IsPending = true;
    }
}

现在我想使用属性 PaperChecking.IsCheckingPending 在我的 WPF 窗口中显示一些控件。我已将同一属性与控件的可见性绑定在一起。首次加载窗口时,行为是预期的,因为集合已经存在。但是在运行时,当我更改 Paper 对象的待处理状态时,如下所示:

    ChangePaperSetting changePaperSetting = new ChangePaperSetting();
    changePaperSetting.SelectPaper("Paper1");
    changePaperSetting.SelectPaper("Paper2");
    changePaperSetting.SelectPaper("Paper5");

在我的收藏中,现在我有论文将IsEnding视为真实。所以现在 PaperChecking.IsCheckingPending 将返回 TRUE,根据该值,我的控件现在应该可见。

在普通对象中,我本可以实现 INotifyPropertyChanged ,但在上述情况下,我在属性上没有 Setter。有没有办法做到这一点或任何其他使用相同类结构的整洁方法。

//-------------------------------------------------------------------------------//

更新

正如乔希所建议的,我尝试了这样的事情:

/// <summary>
/// Paper Class
/// </summary>
public class Paper : INotifyPropertyChanged
{
    public string PaperName { get; set; }
    private bool isPending;
    public bool IsPending
    {
        get
        {
            return isPending;
        }
        set
        {
            if (isPending != value)
            {
                isPending = value;
                PropertyChanged(this, new PropertyChangedEventArgs("IsPending"));
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}
/// <summary>
/// PaperChecking class, Individual papers will be processed here.
/// </summary>
public class PaperChecking : Control
{
    public static List<Paper> listOfPapers { get; set; }
    public static bool IsCheckingPending
    {
        get
        {
            //List has items and it is not null, so intentionally removed the checks.
            try
            {
                return listOfPapers.Count(paper => paper.IsPending == true) > 0 ? true : false;
            }
            catch (Exception ex) { return false; }
        }
    }

    public static event PropertyChangedEventHandler PropertyChanged;
    public  static void PendingStatusChanged(object sender,PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsPending")
        {                
           //If I keep it static, It given Null Reference Error 
           //and If I implement INotifyPropertyChanged interface 
           //in this Class, it gives compilation error because 
           //I am doing so in my Static property.
            PropertyChanged(null,new PropertyChangedEventArgs("IsCheckingPending"));
        }
    }
}
/// <summary>
/// This class will select papers for processing
/// </summary>
public class ChangePaperSetting
{
     public static void AddPapers()
    {
       var listOfPapers = new List<Paper>();
        for (int i = 0; i < 5; i++)
        {
            var paper = new Paper() { PaperName = "Paper"+i.ToString(), 
                                      IsPending = false };
            paper.PropertyChanged+=PaperChecking.PendingStatusChanged;
            listOfPapers.Add(paper);
        }
        PaperChecking.listOfPapers = listOfPapers;
    }
    public void SelectPaper(string paperName)
    {
        //It can be assumed that Paper object will never be NULL
        PaperChecking.listOfPapers.FirstOrDefault(paper => paper.PaperName.Equals(paperName)).IsPending = true;
    }
}

这是我的 XAML 代码:

<Window xmlns:my="clr-namespace:LearningWpf"  x:Class="LearningWpf.Window4"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window4" Height="300" Width="300"     
    >
<Window.Resources>
    <my:PaperChecking x:Key="paperChecking"/>
    <BooleanToVisibilityConverter x:Key="bvc" />
</Window.Resources>
<StackPanel>
    <Button Name="btn1" Content="Button1" Height="20" Width="80" Click="btn1_Click"></Button>
    <Button Name="btn2" Content="Button2" Height="20" Width="80" Click="btn2_Click"></Button>
    <Button Name="btn3" Content="Button3" Height="20" Width="80" 
            Visibility="{Binding Source={StaticResource paperChecking},
                         Path=IsCheckingPending,
                         Converter={StaticResource bvc}}"></Button>
</StackPanel>

这是我的代码隐藏.cs

public partial class Window4 : Window
{
    public Window4()
    {
        InitializeComponent();
    }
    private void btn1_Click(object sender, RoutedEventArgs e)
    {
        ChangePaperSetting.AddPapers();
    }
    private void btn2_Click(object sender, RoutedEventArgs e)
    {
        var v = PaperChecking.listOfPapers.FirstOrDefault(paper => 
                 paper.PaperName == "Paper1");
        v.IsPending = true;
    }
}

但是这段代码给出了错误,很严格,因为我在没有初始化它的情况下使用了静态变量。如果有任何更正或任何其他方法来实现相同的目标。非常感谢您的帮助。

使用“仅获取”静态属性作为绑定源

由于您使用的是 CLR 属性,因此您有责任通知 UI 基础绑定属性已更改,这只能通过从代码中引发 PropertyChanged 事件来实现。首先,使集合ObservableCollection,因为它实现了INotifyCollectionChanged和INotifyPropertyChanged。将集合更改事件与您的集合挂钩,并在处理程序中简单地为您的属性引发属性更改事件,如下所示 -

ObservableCollection<Paper> listOfPapers =  new ObservableCollection<Paper>();
listOfPapers.CollectionChanged += new NotifyCollectionChangedEventHandler(listOfPapers_CollectionChanged);
void listOfPapers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
       OnPropertyChanged("IsCheckingPending");
}

使用这种方法,您不必担心是否需要从 SelectPaper(( 以外的方法在集合中添加项目。

其他可能的解决方案可能是使用Dependency Property而不是普通的 CLR 属性,这样您就不必担心显式更改属性。

http://msdn.microsoft.com/en-us/library/ms752914.aspx

也许是

这样的东西?

private static bool _isCheckingPending;
public static bool IsCheckingPending 
{ 
    get 
    {             
        bool pending = ListPapers.Count(paper => paper.IsPending == true) > 0;
        if (pending != _isCheckingPending) 
        {
           PropertyChanged("IsCheckingPending");
           _isCheckingPending = pending;
        }
        //List has items and it is not null, so intentionally removed the checks. 
        return _isCheckingPending;
    } 
} 

这个想法是它记住上次的结果,如果它与这次的结果不同,则引发 PropertyChanged 事件(当然,您将实现 INotifyPropertyChanged(。

题外话,但为什么 PaperCheck 中的所有属性和函数都是静态的?

但是,要解决您的问题,请添加一个新功能来PaperChecking并实现INotifyPropertyChanged

public void SelectPaper(string paperName)
{
    var paper = ListPapers.FirstOrDefault(paper => paper.PaperName.Equals(paperName));
    if (paper != null)
    {
        paper.IsPending = true;
        PropertyChanged("IsCheckingPending");
    }
}

我假设您知道如何实现INotifyPropertyChanged,并且有自己的方法来引发事件。没有什么说你必须从属性的设置者那里引发事件。

每次查询时,让您的属性获取者循环浏览整个论文列表是非常非常低效的。它可以而且将经常被调用。几乎与任何鼠标或键盘事件有关。每次更改纸张上的待处理状态时,都应尝试缓存计数值。这将需要更多的工作,但可能值得做。

编辑:

实际上对象可以从多个接口更新,它们不会调用相同的方法,它们引用 PAPER 对象并将直接更新属性,而不是在 PaperCheck 类中调用方法

在这种情况下,您应该在 Paper 类上实现INotifyPropertyChanged,然后在 PaperChecking 中侦听这些更新。

public void PaperChanged(object sender, PropertyChangedEventArgs args)
{
    if (args.PropertyName == 'IsPending') PropertyChanged("IsCheckingPending");
}
public void AddPapers()
{
    ListPapers = new List<Paper>();
    ListPapers.Add(new Paper() { PaperName = "Paper1", IsPending = false });
    ListPapers.Add(new Paper() { PaperName = "Paper2", IsPending = false });
    ListPapers.Add(new Paper() { PaperName = "Paper3", IsPending = false });
    ListPapers.Add(new Paper() { PaperName = "Paper4", IsPending = false });
    ListPapers.Add(new Paper() { PaperName = "Paper5", IsPending = false });
    foreach(var paper in ListPapers) 
    {
        paper.PropertyChanged += PaperChanged;
    }
}

还需要将PaperChecking转换为使用实例方法和属性而不是静态方法和属性的类。如果你还不知道MVVM,我建议你阅读它。从本质上讲,您要做的是创建一个PaperChecking实例,并将其设置为视图代码中的DataSource。然后,在 XAML 中,只需像这样绑定:

<Button Name="btn3" Content="Button3" Height="20" Width="80" 
  Visibility="{Binding IsCheckingPending, Converter={StaticResource bvc}}" />

使用 WPF 开始时,静态属性和方法几乎总是错误的。知道什么时候需要使用它们,什么时候你想让自己更轻松。