Xceed WPF Toolkit - BusyIndicator语言 - 在c#中创建动态加载消息,在数据模板中绑定数据.&



我有一个小的WPF应用程序,我正在使用XceedBusyIndicator。我在动态更新加载消息时遇到了一些麻烦,因为内容包含在DataTemplate。我所熟悉的用于数据绑定或设置文本值的方法不起作用。

我做了一些研究,看起来其他人也有这个问题。它似乎在这里得到了回答,但因为答案不在上下文中,我不能完全弄清楚这将如何在我的代码中工作。

这是我的示例代码,如果有人能帮助我了解我错过了什么,我会非常感激。这增加了使用BackgroundWorker的挑战线程。我使用此方法是因为我预计这将是一个长时间运行的过程——最终该操作将启动一个SQL Job,该Job将处理最多可能需要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();
}
}
}

感谢您的审阅,感谢您给予的任何帮助或指导。

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()
{
...
}
}

你可以直接绑定到DataTemplate中的属性。

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