【问题标题】:What is the worst gotcha in WPF?WPF 中最糟糕的问题是什么?
【发布时间】:2011-02-10 13:26:14
【问题描述】:

我已经开始给自己列一个“WPF 陷阱”列表:让我烦恼的事情,我不得不写下来记住,因为我每次都爱上它们......

现在,我敢肯定你们都曾在某一时刻偶然发现过类似情况,我希望你们分享您在这个主题上的经验:

一直困扰着你的问题是什么?你觉得最讨厌的那个?

(我有几个问题似乎没有解释,也许你的意见会解释)

以下是我的一些“个人”陷阱(随机呈现):

  1. 即使单击是在控件的“透明”背景(例如标签)上而不只是在内容(在本例中为文本)上也能触发 MouseEvent,控件的背景必须设置为“Brushes.Transparent”,而不仅仅是“null”(标签的默认值)

  2. WPF DataGridCell 的 DataContext 是单元格所属的 RowView,而不是 CellView

  3. 在 ScrollViewer 内部时,Scrollbar 由 scrollviewer 本身管理(即设置 ScrollBar.Value 等属性无效)

  4. 当你按下“F10”时Key.F10不会被触发,而是你得到Key.System,你必须去寻找e.SystemKey来得到Key.F10

...现在你开始了。

【问题讨论】:

  • @ChrisF:以问题的形式......“有哪些 WPF 陷阱?”然后将其标记为社区 wiki。我们有很多这类问题。
  • @emddudley - a) 只有版主现在可以提出问题 CW,b) 它仍然是“x 列表”问题,c) 规则已经收紧,因为这些问题中的大多数(如果不是全部)被问到了。
  • 我个人觉得这种问题很有启发性。例如,我在这方面学到了很多东西:stackoverflow.com/questions/241134/…,我认为拥有相同类型的 WPF 知识库是个好主意(关于其他主题有一些这样的问题)。我重新表述了这个问题,使它看起来像一个问题,以防它真的只是问题被提出的方式。
  • Key.F10 不是“陷阱”。这就是 Windows 的工作方式。 F10 键用于为键盘用户设置菜单栏的焦点。如果它难以覆盖,希望应用程序开发人员不会弄乱它。它被称为SystemKey 是出于原因,而不仅仅是为了绊倒你。
  • 啊,是的,但我不认为有任何“陷阱”是为了“绊倒我”;)我希望 MS 的人不要那么邪恶......我只是在说明当您处理击键时,必须在与其他 F 键完全不同的地方寻找 F10 键,这是非常违反直觉的(如果您必须使用 F10 键,这是我的情况,不要责怪我这个,我也不喜欢这个)

标签: wpf


【解决方案1】:
  1. 始终观察输出窗口 绑定错误。忽略输出 窗户是催泪的秘诀。

  2. 在绑定中使用 PresentationTraceOptions.TraceLevel="High" 在调试绑定失败时获取详细的绑定信息。

  3. 制作静态、不变的资源,例如画笔 PresentationOptions:Freeze="True" 以在运行时节省资源。

  4. 将 WPF DataGrid 用作数据网格。修改它以使其表现得像 Excel 是一件非常痛苦的事情。

  5. BindingList<T> 不能很好地与 CollectionViewSource 配合使用。改为从您的视图模型中公开ObservableCollection<T>

  6. 互联网提供了六种不同的想法,用于在 WPF 文本框中显示 CueBanner 文本。它们都坏了。

【讨论】:

  • 第一个+1(如果可以的话,我会加2:))。我根本不知道第二个。非常有趣,虽然这不是一个真正的问题。 + 1 也适用于 4...(我正在这样做:/)
  • @David:是的,我也是。这是一个不断与 DataGrid 实现键盘焦点斗争的练习。
  • 当绑定出现问题时,输出窗口可以成为一个巨大的救星。
  • 在 WPF 文本框中提示横幅文本的各种方法存在哪些问题?
  • 在 wpf 文本框中可靠地显示水印/提示横幅文本的唯一技术是手动将带有文本的文本块注入文本框的可视化树。这依赖于文本框可视树的特定方面,但它在不同的环境中始终如一地工作,因为您为横幅文本保持正确的 z 顺序。使其成为附属财产的奖励积分。
【解决方案2】:

1) 当我从 WinForms 进行转换时,每半小时就会收到一次:在 UI 上放置随机文本时使用 TextBlock instead of Label(或者根本不使用任何标签,如果文本是静态)!

2)DataTriggers/Triggers不能放入Control.Triggers,必须放入Control.Styles/Style/Style.Triggers

3) 如果属性要被 XAML 识别为集合属性,则属性的类型必须实现 IList,而不是 IList<T>

4) 绑定捕获异常。

5) 使用单例转换器/静态转换器类,因此您不必每次使用时都创建新转换器。

6) 必须明确指定 DependencyProperty 的默认值类型:0u 为 uint,(float) 0 为浮点数,0.0 为双精度...

7) It matters if the control's property definitions are before or after its content.

8) 切勿使用PropertyMetadata 设置引用类型DependencyProperty 的默认值。相同的对象引用将分配给所属类的所有实例。

【讨论】:

    【解决方案3】:

    刚开始的时候,让我着迷的主要问题是

    • 由于忘记而未更新列表 使用 ObservableCollection
    • 属性也未更新 忘记添加 OnPropertyChanged 或错误地键入属性 姓名

    最近我偶然发现了这些问题

    【讨论】:

      【解决方案4】:

      ToolTips 和 ContextMenus 不共享其所有者的 DataContext?我认为这首先会吸引所有人

      【讨论】:

        【解决方案5】:
        • 如果启用,Button.IsCancelfalse 分配给 Window.DialogResultButton.IsDefault 没有。
          它们是如此相似,对我来说,起初看起来很直观,两者都应该关闭对话。我通常会破坏 MVVM 并在代码隐藏中修复它

        • Button.IsCancel + Command = 对话框不会关闭(Window.DialogResult 未分配)但Command 会执行
          据我了解:如果IsCancel 的优先级高于Command,那么在 Esc 上,它会将“false”分配给DialogResult,并且不会调用Command。或者,如果Command 具有更高的优先级,那么它将首先被调用并分配DialogResult。不明白是怎么跳过的?

        • 绑定处理异常!
          它不仅在调试时浪费时间,从 OOP 的角度来看也是错误的,因为如果抛出异常,则意味着我们系统中某处发生了异常情况(从错误的数据供应到未经授权的内存访问故障),所以它可以是只有在你知道该怎么做的情况下才能处理。你不能只是 catch(Exception){} 抓住他们然后忽略。如果程序中有未知异常,它应该通知用户,登录并关闭,而不是假装一切正常......

        • HeaderContent 只能有一个子控件并且没有填充
          所有东西都应该有填充,甚至逻辑控件(容器),对吧?我认为这是不一致的。你怎么看?

        • 如果您通过FocusManager.FocusedElement 将焦点设置为ListBox,您仍然无法使用键盘切换其内容,因为焦点设置为ListBoxes 框架而不是它的内容。我想我不知道其他 UI API 会向 UI 程序员公开控件框架之类的东西,它应该被我们封装,因为抽象的 ListBox 代表一个列表,它只是一个事物列表,而不是一个盒子中的事物列表。 好吧,它的名称中有框,但仍然......我们在这里几乎有两个不同的控件。 MVVM not breaking fix

        • ListBox.IsSynchronizedWithCurrentItem 默认为false,因此如果您将不同的值或null 分配给ItesSource,那么SelectedItem 仍然保持旧值,直到用户从新列表中选择某些内容。例如,它可能会搞砸CanExecute。每次都需要手动设置。

        • PasswordBox 中没有暴露的绑定会导致时间浪费和肮脏的黑客攻击......但它仍然有一个字符串属性PasswordBox.Password 暴露所以甚至不要试图争论安全性因为 Snoop。 ..

        • 这不是一个陷阱,但表格布局是如此 IE6 IMO。容器设计有助于将内容与其布局分开。
          因为每次我需要在某些地方更改某些内容时,我都需要弄乱Grid.RowGrid.Column。是的,我们有DockPanelStackPanel 和其他人,但您不能在其中进行一些列对齐。 (和DockPanel 就像完全独立的陷阱) 如果UniformGrid 可以更可定制,我认为那将是理想的。您总是需要在 Grid 和 Panels 之间进行选择,通常,如果您获得了一些东西,就会失去一些东西。

        【讨论】:

        • 您不会通过在代码隐藏中关闭窗口来破坏 MVVM。如果您的视图被 HTML 页面替换,您将重定向而不是关闭窗口。关闭/重定向的代码属于视图,而不是模型。 MVVM != 没有代码隐藏
        【解决方案6】:

        上周我得到了一个漂亮的:

        当模板化 RichTextBox 时,模板内的事件处理遵循一条与隧道或冒泡无关的奇怪路线

        例如:对于应该隧道的事件:事件首先通过 ContentPresenter 隧道,然后从模板顶部隧道返回。

        my question on the subject

        【讨论】:

          【解决方案7】:

          在 WPF 中没有干净的方法来处理验证,我不喜欢 IDataErrorInfo 默认提供的魔术字符串:

            public string this[string columnName]
            {
                  if (columnName == "FirstName")
                  {
                      if (string.IsNullOrEmpty(FirstName))
                          result = "Please enter a First Name";
                  }
            }
          

          但是,我已经尝试了许多框架,例如 SimpleMVVM、FluentValidation 和 MVVMValidation,而 BY FAR MVVM 验证是最好的方法,例如:

          Validator.AddRule(() => RangeStart,
                        () => RangeEnd,
                        () => RuleResult.Assert(RangeEnd > RangeStart, "RangeEnd must be grater than RangeStart");
          

          【讨论】:

          • 没有内置的干净方式 - 我不会称之为陷阱。请记住,整个 XAML 和绑定支持只是一堆“魔术字符串”、反射和缓存表达式。您可以很高兴地从表达式 stackoverflow.com/a/672212/286976 中提取属性名称,在 .net 4.5 中您可以使用 [CallerMemberName] msdn.microsoft.com/en-us/library/…
          • INotifyDataErrorInfo 也是一个更简洁的 WPF 接口。
          【解决方案8】:

          我个人最喜欢的是这个:

          public double MyVariable
          {
              get { return (double)GetValue(MyVariableProperty); }
              set { SetValue(MyVariableProperty, value); }
          }
          public static readonly DependencyProperty MyVariableProperty = DependencyProperty.Register(
              "MyVariable", typeof(double), typeof(MyControl), new UIPropertyMetadata(0));
          

          试试吧,一旦声明了这个属性,它就会崩溃。为什么?因为显然不能使用反射将 0 分配给双精度数。

          不是一个真正的陷阱,而是一个建议:使用 Snoop 或类似的东西,如果你不使用它,你一定是疯了......我告诉你疯了!

          【讨论】:

            【解决方案9】:

            Binding.StringFormat 仅在目标属性的类型为string 时有效。

            【讨论】:

              【解决方案10】:
              • TreeViewSelectedItem 属性不可设置。相反,您必须将 TreeViewItemIsSelected 属性绑定到项目的视图模型并在那里设置您的选择。

              • ListBox 的 SelectedItem,另一方面 可设置的,但 item selection 不等于 item focus。如果您想实现正确的键盘导航以及从视图模型中选择项目,则必须实现手动焦点修复,例如:

                public void FixListboxFocus()
                {
                    if (lbFiles.SelectedItem != null)
                    {
                        lbFiles.ScrollIntoView(lbFiles.SelectedItem);
                        lbFiles.UpdateLayout();
                
                        var item = lbFiles.ItemContainerGenerator.ContainerFromItem(viewModel.SelectedFile);
                        if (item != null && item is ListBoxItem listBoxItem && !listBoxItem.IsFocused)
                            listBoxItem.Focus();
                    }
                }
                

                ...并在每次从视图模型中更改所选项目时调用它:

                SelectedFile = files.FirstOrDefault();
                viewAccess.FixListboxFocus();
                

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2014-09-07
                • 1970-01-01
                • 1970-01-01
                • 2017-03-12
                • 1970-01-01
                • 1970-01-01
                • 2010-10-08
                • 1970-01-01
                相关资源
                最近更新 更多