我遇到了 ContentControl 的内容问题,其内容是使用 DataTriggers 设置的,将内容随机重置为 DataTemplate 中指定的默认内容。
场景是我在网络上有一堆设备(传感器),我需要检查它们的状态等等。根据传感器所处的状态,我可能想要显示彩色圆圈(绿色、红色或黄色)或图像。例如,如果有人正在使用传感器,我想显示代表用户的图像。如果传感器可用于连接,我想显示一个绿色椭圆等。
我目前正在使用 WPF DataGrid 来显示传感器列表及其状态,尽管我使用 ListBox 和 ListView 得到了相同的错误行为(没有尝试过普通的 ItemsControl)。仅供参考,传感器来来去去是异步的。
如果您运行示例代码,您将看到最初连接状态为 CONNECTED 的项目将首先显示所需的图像。随着行添加到网格中,图像会随机消失并替换为 DataTemplate 中指定的默认内容。仅当内容中有图像时才会出现此问题。其他州工作得很好。
以下是我认为您需要查看行为的所有代码(xaml、viewmodel、模型)。很抱歉下面发布的代码量。我试图尽可能地把它配对来说明这个问题。希望通过查看 XAML,问题很明显。如果您愿意,剩余的源代码将帮助您更快地启动和运行它。
这是窗口 XAML:
<Window x:Class="StackOverflowGridIssue.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:StackOverflowGridIssue.Model"
xmlns:shape="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Width="500"
Title="MainWindow" >
<Grid>
<DataGrid Name="_SensorsDataGrid" ItemsSource="{Binding Sensors}"
AutoGenerateColumns="False" HeadersVisibility="Column" >
<DataGrid.Columns>
<!-- Status -->
<DataGridTemplateColumn Header="Status" MinWidth="50"
Width="SizeToHeader" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl x:Name="myContent" Background="LimeGreen"
Width="25" Height="25">
<ContentControl.ToolTip>
<TextBlock Text="{Binding ConnectionState, Mode=OneWay}"
Foreground="Black" />
</ContentControl.ToolTip>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}" BasedOn="{x:Null}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Ellipse Height="10" Width="10"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding Background}" />
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContentControl.Style>
</ContentControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.NOT_FOUND}">
<Setter TargetName="myContent" Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.AVAILABLE}">
<Setter TargetName="myContent" Property="Background"
Value="GREEN" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.CONNECTED}">
<Setter TargetName="myContent" Property="Content">
<Setter.Value>
<Image Source="Images/User.png" />
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- DEBUG Connection State -->
<DataGridTextColumn Header="DEBUG"
Binding="{Binding ConnectionState}" Width="SizeToCells" />
<!-- Sensor Name -->
<DataGridTextColumn Header="Sensor Name"
Binding="{Binding Name}" Width="SizeToCells" />
<!-- IPAddress -->
<DataGridTextColumn Header="IP Address"
Binding="{Binding IPAddress}" Width="SizeToCells" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
这是 App.xaml.cs ,我在其中引导所有内容并模拟异步发现的传感器(仅供参考,如果我连续加载它们会出现同样的问题,更容易看到传感器何时一次缓慢加载:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public const int NUM_SENSORS = 100;
Random _connectionStateGenerator = new Random();
ConnectionStateType _connectionState = ConnectionStateType.AVAILABLE;
SensorViewModel viewModel;
Timer _timer = new Timer(300);
int _index = 1;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Get a handle to the main view (MainWindow)
Window window = new MainWindow();
viewModel = new SensorViewModel();
//Loads sensors synchronously (same issue)
//AddSensors(viewModel.Sensors);
window.DataContext = viewModel;
window.Show();
//Simulate async sensor discovery (more real world example)
_timer.Enabled = false;
_timer.AutoReset = true;
_timer.Elapsed += (s, args) =>
{
Dispatcher.InvokeAsync(new Action( () =>
{
viewModel.Sensors.Add(
new Sensor("Sensor" + _index, "192.168.1." + _index,
(ConnectionStateType)_connectionStateGenerator.Next(0, 3)));
if (_index++ > NUM_SENSORS)
_timer.Enabled = false;
}));
};
_timer.Enabled = true;
}
//Helper for loading synchronously rather than asynchronously
private void AddSensors(ObservableCollection<Model.Sensor> sensors)
{
for (int i = 0; i < NUM_SENSORS; i++)
{
_connectionState = (ConnectionStateType)_connectionStateGenerator
.Next(0, 5);
sensors.Add(
new Sensor("Sensor" + i, "192.168.1." + i, _connectionState));
}
}
}
这是代表传感器的型号代码:
public enum ConnectionStateType
{
NOT_FOUND,
AVAILABLE,
CONNECTED,
}
public class Sensor : INotifyPropertyChanged
{
string _name = "Unknown";
string _IPAddress;
ConnectionStateType _connectionState = ConnectionStateType.AVAILABLE;
public Sensor(string name, string IPAddress, ConnectionStateType connectionState)
{
_name = name;
_IPAddress = IPAddress;
_connectionState = connectionState;
}
public ConnectionStateType ConnectionState
{
get { return _connectionState; }
set
{
if (value == _connectionState) return;
_connectionState = value;
NotifyPropertyChanged("ConnectionState");
}
}
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
NotifyPropertyChanged("Name");
}
}
public string IPAddress
{
get { return _IPAddress; }
set
{
if (value == _IPAddress) return;
_IPAddress = value;
NotifyPropertyChanged("IPAddress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
}
这是视图模型:
public class SensorViewModel : INotifyPropertyChanged
{
ObservableCollection<Sensor> _sensors = new ObservableCollection<Sensor>();
public ObservableCollection<Sensor> Sensors
{
get { return _sensors; }
private set
{
if (value == _sensors) return;
_sensors = value;
NotifyPropertyChanged("Sensors");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
}
谢谢你的帮助。