Xceed WPF Toolkit - BusyIndi​​cator - 在 C# 中创建动态加载消息,数据模板中的数据绑定

Posted

技术标签:

【中文标题】Xceed WPF Toolkit - BusyIndi​​cator - 在 C# 中创建动态加载消息,数据模板中的数据绑定【英文标题】:Xceed WPF Toolkit - BusyIndicator - creating dynamic loading messages in C#, data binding in a data template 【发布时间】:2021-11-09 18:41:05 【问题描述】:

我正在开发一个使用 Xceed BusyIndi​​cator 的小型 WPF 应用程序。我在动态更新加载消息时遇到了一些问题,因为内容包含在 DataTemplate 中。我熟悉的数据绑定或设置文本值的方法不起作用。

我已经进行了一些研究 - 看起来其他人也遇到了这个问题。似乎是answered here,但由于答案不在上下文中,我无法完全弄清楚它在我的代码中是如何工作的。

这是我的示例代码,如果有人可以帮助我了解我所缺少的内容,我将不胜感激。这增加了使用 BackgroundWorker 线程的挑战。我使用它是因为我预计这将是一个长期的进程——最终该操作将启动一个 SQL 作业,该作业将处理可能需要长达 15 分钟的项目。我的计划是让线程定期运行存储过程来获取剩余项目的计数以处理和更新加载消息。

MainWindow.xaml:

<Window x:Class="WPFTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        xmlns:local="clr-namespace:WPFTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <xctk:BusyIndicator x:Name="AutomationIndicator">
        <xctk:BusyIndicator.BusyContentTemplate>
            <DataTemplate>
                <StackPanel Margin="4">
                    <TextBlock Text="Sending Invoices" FontWeight="Bold" HorizontalAlignment="Center"/>
                        <WrapPanel>
                            <TextBlock Text="Items remaining: "/>
                        <TextBlock x:Name="_ItemsRemaining" Text="Binding Path=DataContext.ItemsRemaining, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Window"/>
                        </WrapPanel>
                    

                </StackPanel>
            </DataTemplate>
        </xctk:BusyIndicator.BusyContentTemplate>
        <Grid>
            <StackPanel>
                <TextBlock Text="Let's test this thing" />
                <Button x:Name="_testBtn" Content="Start" Width="100" HorizontalAlignment="Left" Click="testBtn_Click"/>
                <TextBlock Text="Binding ItemsRemaining"/>
            </StackPanel>
        </Grid>
    </xctk:BusyIndicator>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFTest

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    
        
        public MainWindow()
        
            InitializeComponent();
            UpdateItemsRemaining(0);
        


        public class ItemCountDown
        
            //One Idea was to try and set a data binding variable
            public string ItemsRemaining  get; set; 
        

        public void UpdateItemsRemaining(int n)
        
            ItemCountDown s = new ItemCountDown();
            
                s.ItemsRemaining = n.ToString();
            ;
            //this.AutomationIndicator.DataContext = s;      Works during initiation, but not in the thread worker.



    


        private void testBtn_Click(object sender, RoutedEventArgs e)
        
            //Someone clicked the button, run the Test Status 
            TestStatus();
        


        public void TestStatus()
        
            // Normally I'd start a background worker to run a loop to check that status in SQL
            BackgroundWorker getStatus = new BackgroundWorker();
            getStatus.DoWork += (o, ea) =>
            

                //Normally there's a sql connection being opened to check a SQL Job, and then I run a loop that opens the connection to check
                //the status until it either fails or successfully ended.  

                //but for this test, I'll just have it run for 15 seconds, counting down fake items.

                int fakeItems = 8;

                do  //
                

                    //Idea One - write to the text parameter.  But can't find it in the template
                    //Dispatcher.Invoke((Action)(() => _ItemsRemaining.Text = fakeItems));

                    //Idea two - use data binding to update the value.  Data binding works just find outside of the Data Template but is ignored in the template
                    UpdateItemsRemaining(fakeItems);


                    //subtract one from fake items and wait a second.
                    fakeItems--;
                    Thread.Sleep(1000);

                 while (fakeItems > 0);


            ;

            getStatus.RunWorkerCompleted += (o, ea) =>
            
                //work done, end it.
                AutomationIndicator.IsBusy = false;

            ;

            AutomationIndicator.IsBusy = true;
            getStatus.RunWorkerAsync();

        
    


感谢您的审阅,感谢您提供的任何帮助或指导。

【问题讨论】:

【参考方案1】:

DataContext 设置为您的ItemCountDown 对象并实现INotifyPropertyChanged

public class ItemCountDown : INotifyPropertyChanged

    private string _itemsRemaining;
    public string ItemsRemaining
    
        get  return _itemsRemaining; 
        set  _itemsRemaining = value; NotifyPropertyChanged(); 
    

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));


public partial class MainWindow : Window

    private readonly ItemCountDown s = new ItemCountDown();

    public MainWindow()
    
        InitializeComponent();
        DataContext = s;
        UpdateItemsRemaining(0);
    

    public void UpdateItemsRemaining(int n)
    
        s.ItemsRemaining = n.ToString();
    


    private void testBtn_Click(object sender, RoutedEventArgs e)
    
        TestStatus();
    

    public void TestStatus()
    
        ...
    

然后您可以直接绑定到 XAML 标记的 DataTemplate 中的属性:

<TextBlock x:Name="_ItemsRemaining" Text="Binding Path=DataContext.ItemsRemaining, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Window"/>

【讨论】:

mm8!你是我的英雄。谢谢!我必须使用 XAML 中的相对源绑定才能使其在 内工作,但效果很好。

以上是关于Xceed WPF Toolkit - BusyIndi​​cator - 在 C# 中创建动态加载消息,数据模板中的数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

如果不为空,则 NullToVisibilityConverter 使可见

WPF 开源控件库Extended WPF Toolkit介绍(经典)

WPF toolkit AutoCompleteBox

WPF 使用 Microsoft.Toolkit.Wpf.UI.Controls 的 InkCanvas 时加上背景色和按钮方法

使用 Microsoft.Toolkit.Mvvm 和 Microsoft.Xaml.Behaviors.Wpf 将事件参数传递给命令

WPF 使用 Microsoft.Toolkit.Wpf.UI.Controls 的 InkCanvas 时加上背景色和按钮方法