C# 使用ObservableCollection中的活动任务数更新Textblock

C# 使用ObservableCollection中的活动任务数更新Textblock,c#,wpf,observablecollection,C#,Wpf,Observablecollection,我试图创建一个状态窗口,向用户显示当前正在运行的任务数量。每个任务都被添加到ObservableCollection中,我在窗口XAML中设置了绑定路径,但该框不会更新。我已经广泛地搜索了一个关于如何实现这一点的好的工作示例或教程,但我找不到任何东西。我做错了什么?顺便说一下,这是一个连接到我们办公室的每个Cisco交换机并下载配置文件的程序 窗口XAML: <Window x:Class="BackupCiscoConfigs.ProcessRunning" xmlns=

我试图创建一个状态窗口,向用户显示当前正在运行的任务数量。每个任务都被添加到ObservableCollection中,我在窗口XAML中设置了绑定路径,但该框不会更新。我已经广泛地搜索了一个关于如何实现这一点的好的工作示例或教程,但我找不到任何东西。我做错了什么?顺便说一下,这是一个连接到我们办公室的每个Cisco交换机并下载配置文件的程序

窗口XAML:

<Window x:Class="BackupCiscoConfigs.ProcessRunning"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:BackupCiscoConfigs"
        Title="ProcessRunning" Height="300" Width="300"
        Closed="Window_Closed" ResizeMode="CanMinimize">

    <Grid>
        <Button Content="Run" Height="23" HorizontalAlignment="Center" Margin="0,0,0,0" Name="btnRun" VerticalAlignment="Bottom" Width="75" Click="btnRun_Click" />
        <TextBlock Width="200" Height="85" Margin="35,80,43,65" Text="{Binding Mode=OneWay, Path=d1.results.Count}"></TextBlock>
    </Grid>
</Window>

窗口代码隐藏:

namespace BackupCiscoConfigs
{
    /// <summary>
    /// Interaction logic for ProcessRunning.xaml
    /// </summary>
    public partial class ProcessRunning : Window
    {
        private MainWindow m_parent;
        private Configuration currentConfig;
        public DeviceInterface di;

        public ProcessRunning(MainWindow parent)
        {
            currentConfig = Configuration.loadConfig();
            m_parent = parent;
            InitializeComponent();
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            List<Device> devices = currentConfig.devices;
            di = new DeviceInterface(currentConfig.tftpIP,
                currentConfig.tftpDIR, currentConfig.cmd);
            di.RunCommands(devices);
        }
    }
}
namespace BackupCiscoConfigs
{
    /// <summary>
    /// Interaction logic for ProcessRunning.xaml
    /// </summary>
    public partial class ProcessRunning : Window, INotifyPropertyChanged
    {
        private MainWindow m_parent;
        private Configuration currentConfig;
        private DeviceInterface di;
        public event PropertyChangedEventHandler PropertyChanged;

        // This method is called by the Set accessor of each property. 
        // The CallerMemberName attribute that is applied to the optional propertyName 
        // parameter causes the property name of the caller to be substituted as an argument. 
        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private ObservableCollection<Task> _results;
        public ObservableCollection<Task> Results
        get
        {
            return _results;
        }
        set
        {
            _results= value;
            NotifyPropertyChanged("results");
        }

        public ProcessRunning(MainWindow parent)
        {
            currentConfig = Configuration.loadConfig();
            m_parent = parent;
            InitializeComponent();
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            List<Device> devices = currentConfig.devices;
            di = new DeviceInterface(currentConfig.tftpIP,
                currentConfig.tftpDIR, currentConfig.cmd);
            di.RunCommands(devices);
            Results = di.results;
        }
    }
}
namespace backupciscoconfig
{
/// 
///ProcessRunning.xaml的交互逻辑
/// 
公共部分类ProcessRunning:窗口
{
私有主窗口m_父窗口;
私有配置currentConfig;
公共设备接口di;
公共进程正在运行(主窗口父级)
{
currentConfig=Configuration.loadConfig();
m_parent=父母;
初始化组件();
}
私有void btnRun\u单击(对象发送方,路由目标)
{
列出设备=currentConfig.devices;
di=新设备接口(currentConfig.tftpIP,
currentConfig.tftpDIR、currentConfig.cmd);
di.运行命令(设备);
}
}
}
类生成任务:

namespace BackupCiscoConfigs
{
    public class DeviceInterface
    {
        private string tftpIP;
        private string tftpDIR;
        private string command;
        private string dirDate;
        public ObservableCollection<Task> results { get; set; }

        public DeviceInterface(string tftpIP, string tftpDIR, string cmd)
        {
            this.tftpIP = tftpIP;
            this.tftpDIR = tftpDIR;
            this.command = cmd;
            dirDate = DateTimeOffset.Now.ToString("MM.dd.yyyy.HH.mm.ss");
            Directory.CreateDirectory(tftpDIR + dirDate);
        }

        public void RunCommands(List<Device> devices)
        {
            results = new ObservableCollection<Task>();
            foreach (Device d in devices)
            {
                Device d1 = d;
                d1.command = command + " tftp://" + tftpIP + "/" + dirDate + "/" + d1.ip + ".cfg";
                results.Add(Task<string>.Factory.StartNew(() => d1.BackupDevice()));
            }
            string res = "";
            foreach (Task<string> t in results)
            {
                string message = t.Result + "\n";
                res += message;
            }
            MessageBoxResult msg = MessageBox.Show(res);
        }
    }
}
namespace backupciscoconfig
{
公共类设备接口
{
私有字符串tftpIP;
私有字符串tftpDIR;
私有字符串命令;
私有字符串dirDate;
公共ObservableCollection结果{get;set;}
公共设备接口(字符串tftpIP、字符串tftpDIR、字符串cmd)
{
this.tftpIP=tftpIP;
this.tftpDIR=tftpDIR;
this.command=cmd;
dirDate=DateTimeOffset.Now.ToString(“MM.dd.yyy.HH.MM.ss”);
CreateDirectory(tftpDIR+dirDate);
}
公共无效运行命令(列出设备)
{
结果=新的可观察集合();
foreach(设备中的设备d)
{
装置d1=d;
d1.command=command+“tftp://”+tftpIP+“/”+dirDate+“/”+d1.ip+“.cfg”;
结果.Add(Task.Factory.StartNew(()=>d1.BackupDevice());
}
字符串res=“”;
foreach(任务t在结果中)
{
字符串消息=t。结果+“\n”;
res+=消息;
}
MessageBoxResult msg=MessageBox.Show(res);
}
}
}

窗口代码隐藏:

namespace BackupCiscoConfigs
{
    /// <summary>
    /// Interaction logic for ProcessRunning.xaml
    /// </summary>
    public partial class ProcessRunning : Window
    {
        private MainWindow m_parent;
        private Configuration currentConfig;
        public DeviceInterface di;

        public ProcessRunning(MainWindow parent)
        {
            currentConfig = Configuration.loadConfig();
            m_parent = parent;
            InitializeComponent();
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            List<Device> devices = currentConfig.devices;
            di = new DeviceInterface(currentConfig.tftpIP,
                currentConfig.tftpDIR, currentConfig.cmd);
            di.RunCommands(devices);
        }
    }
}
namespace BackupCiscoConfigs
{
    /// <summary>
    /// Interaction logic for ProcessRunning.xaml
    /// </summary>
    public partial class ProcessRunning : Window, INotifyPropertyChanged
    {
        private MainWindow m_parent;
        private Configuration currentConfig;
        private DeviceInterface di;
        public event PropertyChangedEventHandler PropertyChanged;

        // This method is called by the Set accessor of each property. 
        // The CallerMemberName attribute that is applied to the optional propertyName 
        // parameter causes the property name of the caller to be substituted as an argument. 
        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private ObservableCollection<Task> _results;
        public ObservableCollection<Task> Results
        get
        {
            return _results;
        }
        set
        {
            _results= value;
            NotifyPropertyChanged("results");
        }

        public ProcessRunning(MainWindow parent)
        {
            currentConfig = Configuration.loadConfig();
            m_parent = parent;
            InitializeComponent();
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            List<Device> devices = currentConfig.devices;
            di = new DeviceInterface(currentConfig.tftpIP,
                currentConfig.tftpDIR, currentConfig.cmd);
            di.RunCommands(devices);
            Results = di.results;
        }
    }
}
namespace backupciscoconfig
{
/// 
///ProcessRunning.xaml的交互逻辑
/// 
公共部分类ProcessRunning:Window,INotifyPropertyChanged
{
私有主窗口m_父窗口;
私有配置currentConfig;
专用设备接口di;
公共事件属性更改事件处理程序属性更改;
//此方法由每个属性的集合访问器调用。
//应用于可选propertyName的CallerMemberName属性
//参数导致调用方的属性名被替换为参数。
私有void NotifyPropertyChanged(字符串propertyName)
{
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
私人可观察收集结果;
公众可观察的收集结果
得到
{
返回结果;
}
设置
{
_结果=价值;
通知财产变更(“结果”);
}
公共进程正在运行(主窗口父级)
{
currentConfig=Configuration.loadConfig();
m_parent=父母;
初始化组件();
}
私有void btnRun\u单击(对象发送方,路由目标)
{
列出设备=currentConfig.devices;
di=新设备接口(currentConfig.tftpIP,
currentConfig.tftpDIR、currentConfig.cmd);
di.运行命令(设备);
结果=di.Results;
}
}
}

我可以立即看到两个问题:

  • 我看不到此路径的设置位置:
    path=d1.results.Count
  • 您正在更新
    results
    属性,但没有实现
    INotifyPropertyChanged
    ,这意味着WPF永远不会知道您已将新的
    ObservableCollection
    挂接到结果中

  • 即使这样,它也不会工作,因为没有像
    d1
    这样的属性。另外,我认为正确的
    DataContext
    绑定将是
    {binding Source={StaticResource DeviceInterface}}
    作为您的示例。啊,是的。我想他想要结果,伯爵。DataContext应该像上面那样工作。直到他在
    结果
    之上实现
    INPC
    ,这有点像我在自己的回答中说的:)我还注意到他没有DeviceInterface的无参数构造函数,所以他不能像你的示例所建议的那样在xaml中实现,而不添加新的构造函数。这个示例需要在无参数构造函数的帮助下。。。不确定要如何实现它。@narohi我在C#4/.NET4上,所以我认为我不能使用[CallerMemberName]属性。我不希望在属性更改时更新属性,而只是在集合更改时更新(集合中的任务总数)。我的理解是,使用ObservableCollection的意义在于它公开INotifyCollectionChanged。我尝试设置DataContext,但仍然不起作用不仅会更改集合。。。它创建了一个n