【问题标题】:C# and WPF loop counter value not changing when used in dispatcherC# 和 WPF 循环计数器值在调度程序中使用时不会改变
【发布时间】:2012-07-11 15:45:59
【问题描述】:

我一直遇到这个奇怪的问题,但我似乎无法找到解决它的方法。

我正在编写 WPF 应用程序并将值加载到 ListBox。具体来说,此列表框显示客户端计算机的引导顺序。所以列表框项目模板包含 4 个值。新序列号(用户更改后的引导顺序)当前(或分配的)序列号(机器现在的顺序)、设备名称以及设备是否启用。

此数据在单独的线程中加载,因此为了将其显示在列表框中,我使用了调度程序。

就这个问题而言,存在一个 List devices ,其中 Device 是一个包含分配序列和设备名称的类。

bootOrderListBox 是 ListBox UI 元素。

当前循环是这样的:

for(int i=0;i<devices.Count();i++)
{
    Device device = devices.ElementAt(i);
    int newSeqNum = (device.AssignedSequence == 0) ? 0 : i + 1;
        bool isEnabled = (device.AssignedSequence == 0) ? false : true;                                    

    Console.WriteLine("\t" + newSeqNum 
                      + "\t" + 
                      device.AssignedSequence 
                      + "\t" + device.DeviceName);
    bootOrderListBox.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
    {

        bootOrderListBox.Items.Add(new BootOrderDevice(device.AssignedSequence, 
                                                       newSeqNum,
                                                       device.DeviceName,
                                                       isEnabled));
    });
}

现在由于这个应用程序的设置方式,我的所有调试都在与我开发的机器不同的机器上,但是所有控制台输出都已重定向到 UI 中的 TextBox(也使用 Dispatcher)。

我的问题是 newSeqNum 在“控制台”和 ListBox 中都是一个常量 1 值,但代码完美地通过了设备列表。

什么可能导致这种情况发生,我可以做些什么来解决它?

我的想法使我相信,当 Dispatcher 运行时,它会在单独的线程上创建一个具有默认值的新 int,或者类似的东西。

非常感谢。

========================更新====================== ===============================

列表框(和控制台)中的输出如下所示:

新序列分配的序列设备名称

1 2 软盘驱动器:1

1 1 USB 存储设备:3

1 3 板载网卡:5

1 4 内置硬盘:2

1 5 CD/DVD/CD-RW 驱动器:4

它肯定会从列表中获取不同的设备。

=========================更新2===================== ============================== 列表框 XAML:

<ListBox Name="bootOrderListBox"
             Grid.Row="2"
             Grid.Column="1"
             Grid.ColumnSpan="4">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Name="newSequenceTextBlock"
                               Text="{Binding Path=NewSequence}"
                               Margin="10,2,2,2" />
                    <TextBlock Name="assignedSequenceTextBlock"
                               Text="{Binding Path=AssignedSequence}"
                               Margin="50,2,2,2" />
                    <TextBlock Name="deviceNameTextBlock"
                               Text="{Binding Path=DeviceName}"
                               Margin="30,2,2,2" />
                    <CheckBox Name="isEnabledCheckBox"
                              IsChecked="{Binding Path=IsEnabled}"
                              Margin="10,2,2,2" 
                              Checked="isEnabledCheckBox_Checked"
                              Unchecked="isEnabledCheckBox_Unchecked"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

它绑定到的 BootOrderDevice 类:

public class BootOrderDevice
{
        public int AssignedSequence { get; set; }

        public int NewSequence { get; set; }

        public string DeviceName { get; set; }

        public bool IsEnabled { get; set; }

        public BootOrderDevice(int assSequence, int newSequence, string deviceName, bool isEnabled)
        {
            AssignedSequence = assSequence;
            NewSequence = newSequence;
            DeviceName = deviceName;
            IsEnabled = isEnabled;
        }
    }

【问题讨论】:

  • 如果 newSeqNum == 1 在每个循环周期中 - 那么 Device device = devices.ElementAt(i) 必须返回相同的 device 实例,您在控制台输出中看到的内容,不同设备或相同设备的转储
  • 顺便说一句,你发布的代码没有任何修改吗???
  • 这是从我的应用程序中复制和粘贴的。最近添加了 BootOrderDevice 类以与 ListBoxItemTemplate 绑定。我计划将来删除设备类。
  • 我认为我们将不得不看看您如何显示分配的序列。它是模板的一部分还是覆盖 ToString()?
  • 第一次调用时 device.AssignedSequence 总是为 0 的地方有什么奇怪的地方吗?

标签: c# wpf multithreading loops dispatcher


【解决方案1】:

我相信这与封闭和价值捕获有关。尝试将 newSeqNum 声明移到 for 循环之外。

【讨论】:

  • 这似乎是调度调用的情况,但console.writeline在这种情况下应该是正确的,不是吗?
  • 实际上 Console.WriteLine() 语句被重定向到 TextStreamWriter,它使用调度程序写入 UI 中的 TextBox
【解决方案2】:

我能看到 newSeqNum 为 1 的唯一方法是:

int newSeqNum = (device.AssignedSequence == 0) ? 0 : i + 1;

编译器是否将其视为:

int newSeqNum = ((device.AssignedSequence == 0) ? 0 : i) + 1;

如果分配的序列是 0,它总是会给你 1。

或者编译器是否将其视为:

int newSeqNum = (device.AssignedSequence == 0) ? 0 : (i + 1);

【讨论】:

  • 是的,这就是我的意图。我希望新序列是 1-5 升序。
  • 但是每个循环循环 i 都会递增,因此 newSeqNum 不会像现在那样是 1 而是 ==i 而不是 i+1 正如预期的那样,出了点问题...
  • 顺便说一句,您关于“编译器如何看待”的假设是错误的,请参阅MSDN。基本上: 之后的所有内容都是一个表达式,它评估并成为结果
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多