使用不同数据模型时WPF绑定不工作

mw3dktmi  于 2023-10-22  发布在  其他
关注(0)|答案(1)|浏览(110)

我正在创建一个应用程序,它监视远程机器上的几个跟踪服务器,并在UI上显示处理后的数据。当我使用模拟器生成数据时,一切都按预期工作。问题是当使用实际的数据模型时,UI没有被更新。有时它在启动后更新一次,有时从不更新。ViewModel保持不变,数据得到正确处理并可用。这会影响所有在XAML中使用绑定的控件。在代码中更新的其他控件按预期工作。ViewModel都正确地实现了INotifyPropertyChanged
请参见以下示例:
TimeStrip user control
在这个TimeStrip控件中,文本框和行应该随着时间向右移动。

UserControl XAML:

<Grid  x:Name="TimeSlotGrid">
         //..
    <local:TimeSlot Grid.Column="2" Text="8:00"/>
    <local:TimeSlot Grid.Column="3" Text="9:00"/>
        //...
</Grid>          
<Grid LayoutUpdated="Grid_LayoutUpdated" Loaded="Grid_Loaded">
    <Canvas x:Name="TimeCanvas">
        <TextBlock Style="{StaticResource MediumTextTheme}" 
                   Text="{Binding Path=UiStuff.CurrentTimeString}" 
                   Canvas.Left="{Binding Path=UiStuff.TimeTextPos}" 
                   Canvas.Top="1"/>
        <Line x:Name="CurrentTimeCursor" 
              X1="{Binding Path=UiStuff.TimePos}" 
              X2="{Binding Path=UiStuff.TimePos}" 
              Y1="0" Y2="360" Stroke="Red" StrokeThickness="2"/>
    </Canvas>
</Grid>

灰框(TimeSlot)用Grid_LayoutUpdated更新,Grid_LayoutUpdated根据当前时间设置其背景梯度:

后面的代码

private void Grid_LayoutUpdated(object sender, EventArgs e)
{
        MainViewModel context = DataContext as MainViewModel;
        context.UiStuff.GridWidth = TimeCanvas.ActualWidth;
        DateTime dt = context.UiStuff.CurrentTime;
        int currentHour = dt.AddHours(-6).Hour;
        double currentHourPart = dt.Minute / 60d;
        for (int i = 0; i < TimeSlotGrid.Children.Count; i++)
        {
            TimeSlot slot = TimeSlotGrid.Children[i] as TimeSlot;
            if (slot is null) continue;
            slot.Visibility = i <= currentHour ? Visibility.Visible : Visibility.Hidden;
            slot.Background = Brushes.Gray;
            if (i == currentHour)
            {
               lgb.GradientStops[1].Offset = currentHourPart;
               lgb.GradientStops[2].Offset = currentHourPart;
               slot.Background = lgb;
             }
         }
}

ViewModel(minimal)

public class MainViewModel 
{
        public UIElements UiStuff { get; set; } 
        private readonly TraceCommunication Trace;
        
        public MainViewModel()
        {
            Trace = new TraceCommunication();
            Trace.MessageReceived += Trace_MessageReceived;
            Trace.StartConnection("10.128.251.136", "9334");
        }
        
        private void Trace_MessageReceived(DataRow message)
        {
            if (!DateTime.TryParse(values[Columns.TimeStamp].ToString(), out DateTime dt)) return;

            UiStuff.CurrentTime = dt;
        }
}

public class UIElements : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        
        private DateTime currentTime;
        public DateTime CurrentTime
        {
            get => currentTime;
            set
            {
                currentTime = value;
                CurrentTimeString = currentTime.ToString("HH:mm");
                double timescale = currentTime.AddHours(-6).TimeOfDay.TotalMinutes / 1440;
                TimePos = (int)(GridWidth * timescale);
                OnPropertyChanged();
            }
        }
        
        private string currenTimeString;

        public string CurrentTimeString
        {
            get => currenTimeString;
            set { currenTimeString = value; OnPropertyChanged();}
        }   
}

正如你在屏幕截图中看到的,绑定在启动后只更新了一次(9:51)。该TimeSlots正确显示(其10:30).
如前所述,当使用模拟器时,这一切都可以工作。唯一的区别在于TraceCommunication对象,它监视远程系统。ViewModel订阅MessageReceived事件并相应地设置UI元素。

public class TraceCommunication
{
        internal delegate void TraceEventHandler(DataRow value);
        internal event TraceEventHandler MessageReceived;
        
        private TraceEngineIntf traceEngine;

        private string _Ip;
        private string _Port; 
        
        public TraceCommunication()
        {
            //...
        }

        internal bool StartConnection(string ip, string port)
        {
            _Ip = ip;
            _Port = port;
            bool success = true;
            if (!ip.Contains("."))
            {
                TraceSimulator Sim = new TraceSimulator(ip);
                Sim.MessageReceived +=  FireEventIfRelevant;
                Sim.StartTracing();
            }
            else
            {
                traceEngine = new TraceEngineIntf();
                traceEngine.ConnectedChanged += Trace_ConnectedChanged;
                traceEngine.MessageReceived += FireEventIfRelevant;
                success = traceEngine.SetHost(_Ip, _Port);
            }
            return success;
        }
        
        private void FireEventIfRelevant(DataRow row)
        {
            if (!TraceFilter.IsRelevant(row)) return;
            MessageReceived?.Invoke(row);
        }
        
        private void Trace_ConnectedChanged(object sender, EventArgs e)
        {
            traceEngine = sender as TraceEngineIntf;

            if (traceEngine.Connected)
            {
                traceEngine.StartRead();
            }
            else
            {
                Connect();
            }
        }
}

在这两种情况下,数据都被正确地接收和处理。这些事件被激发,然后在ViewModel中被正确处理。但是UI仅在使用模拟器时才响应。情况并非总是如此。我开始只用真实的东西,效果很好。后来,当我需要更快的数据进行开发时,我实现了模拟器。
我不知道是什么导致了这种行为。有谁知道发生了什么事,或者可以给予我提示下一步要检查什么?
谢谢你,谢谢

xiozqbni

xiozqbni1#

TimeStrip控件包含24个TimeSlot控件,表示一天中的小时。当我在TimeStripGrid_LayoutUpdated处理程序方法中将当前TimeSlot的BackGround设置为LinearGradientBrush而不是实心Brush时,它就坏了。
我通过将“currentHourPart”作为依赖属性传递给TimeSlot本身并相应地设置渐变来修复它。
我仍然不知道为什么这会导致这个问题,以及为什么当我使用模拟器时它会工作。

相关问题