c# - 双向将 "virtual"字符串列表绑定(bind)到列

标签 c# .net wpf data-binding binding

我有一个字符串列表。

嗯,从概念上讲。它们存储在其他地方,但我想提供一个像列表一样的对象(并在此之上提供任何必要的事件),以及我可以绑定(bind)到的属性。

我想对这些数据建立双向绑定(bind),将其显示为 DataGrid 中的可修改列 .我有以下问题:

  • 我不能进行双向绑定(bind),因为绑定(bind)需要一个路径(即我不能让它看起来像 {Binding}{Binding Path=.} 在列中,如果我得到这个,必须是 {Binding Path=someField"} 才能修改对,这听起来很合理)。
  • 在接口(interface)方面,我不完全知道代理集合对象应该是什么样子(IEnumerable + INotifyCollectionChanged 是否足够?)

  • 是否有任何解决方案不涉及为集合中的每个字符串创建一个代理对象?你能提出一个有效的设计吗?

    为了继续讨论,让我们假设我想绑定(bind)到这样的东西:
    class Source {
        public String getRow(int n);
        public void setRow(int n, String s);
        public int getCount();
        public void addRow(int position, String s);
        public void removeRow(int position);
    }
    

    这不完全是我的情况,但是当我知道如何绑定(bind)到它时,我想我将能够处理这样的任何情况。

    我可以在该 Source 之上提供一个带有任何必要接口(interface)和事件的适配器对象,但我不希望每行数据都有一个适配器对象。

    最佳答案

    虽然为 Source 制作适配器相对清晰,但不幸的是,第二个问题的核心(“不是将每个字符串都包装在一个小对象中”)是 .Net 和 WPF 中内置的冲突。

    第一件事是,WPF 确实为您提供了许多注册“修改数据时”回调的方法,但没有提供注册提供值的回调的方法。我的意思是,“设置”阶段仅可扩展,不可拦截,而“获取”阶段则一无所有。 WPF 将简单地保留并返回它曾经缓存过的任何数据。

    第二件事是在 .Net 中 string是……不可变的。

    现在,如果您直接将字符串作为无路径绑定(bind)或作为任何控件的数据上下文提供,您就会陷入死胡同。问题是,WPF 实际上只传递绑定(bind)的实际值,而没有“它来自哪里”的信息。底层控件将被简单地赋予字符串实例,并且没有理智的方法来修改它,因为字符串不能改变自己。您甚至不会收到有关此类尝试的通知,就像使用只读属性一样。更重要的是 - 如果您曾经设法拦截这样的修改尝试,并且如果您生成一个正确的新字符串,WPF 将永远不会再次要求您提供新值。要更新 UI,您必须手动,字面意思是强制 WPF 重新询问您,例如更改原始绑定(bind)使其指向其他地方(指向新值)或设置数据上下文(指向新实例)。它可以通过一些 VisualTree 扫描来实现,因为每个“更改的”回调都会为您提供 DependencyObjects(控件!),因此您可以向上/向下扫描并篡改它们的属性。请记住该选项 - 稍后我会提到这个。

    因此,一切都归结为这样一个事实,即要获得正常的 2 路绑定(bind),您不必拥有 Path,您“只需要”拥有一个可变的底层数据对象。如果你有一个不可变的 - 那么你必须使用一个绑定(bind)到一个持有不可变值的可变属性..

    话虽如此,如果您想要 ,您只需要将字符串包装起来。修改 他们。

    另一个问题是,如何做到这一点。有很多方法可以做到。当然,您可以像 Joe 和 Davio 建议的那样简单地包装它们(Joe 的注意事项:那里也需要 INotify),或者您可以尝试使用附加的属性和/或行为和/或转换器来执行一些 XAML 技巧来为你。这是完全可行的,例如参见 my other post - 我已经在那里展示了如何“注入(inject)一个虚拟属性”,从其他地方完全提取数据(一个绑定(bind)+转换器即时执行包装,第二个绑定(bind)从附加的包装器中提取值)。通过这种方式,您可以在字符串上创建一个“内容”属性,并且该属性可以简单地返回字符串本身,并且它完全可以双向绑定(bind),没有异常(exception)。

    但是..它不会工作 2-way-ish。

    在你的绑定(bind)/行为/转换链的根部,会有 一个不可变的字符串 .一旦您的智能自动包装绑定(bind)链通过“修改时”回调触发,您将收到一对旧/新值的通知。您将能够将值重新映射到新的和旧的字符串。如果您完美地实现了所有内容,WPF 将简单地使用新值。如果您在某个地方绊倒了,那么您将不得不人为地将新值推送回 UI(请参阅我要求您记住的选项)。所以,没关系。没有包装器,旧值可见,它是可变的,你有新值,UI 显示新值。存储怎么样?

    与此同时,你得到了一个旧/新值对。如果你分析它们,你会得到旧的/新的字符串。但是你如何更新旧的不可变字符串 ?做不到。即使自动包装工作,即使 UI 工作,即使编辑似乎工作,你现在也站在真正的任务上:你的 onmodified 回调被调用,你必须实际更新那个不可变的字符串片段。

    首先,你需要你的来源。它是静态的吗?呼。多么幸运!所以肯定它是实例化的。在 on-modified 回调中,我们只有一个旧的+新的字符串。如何获取 Source 实例?选项:

  • 扫描 VisualTree 并在数据上下文中搜索它并使用找到的任何内容..
  • 添加更多附加属性和绑定(bind)以将虚拟“源”属性绑定(bind)到每个字符串并从新值
  • 中读取该属性

    可行,但有气味,但没有其他选择。

    等等,还有更多:不仅需要旧/新值和 Source 实例!您还需要 ROW INDEX。哦!如何从绑定(bind)数据中获取?再次,选项:
  • 扫描 VisualTree 并搜索它(blaargh)...
  • 添加更多附加属性和绑定(bind)以将虚拟“RowIndex”属性绑定(bind)到每个(blaaergh)...

  • 此时此刻,虽然我看到了这一切 似乎可以实现,实际上可能工作正常 ,我真的认为将每个字符串包裹在一个小
    public class LocalItem // + INotifyPropertyChanged
    {
        public int Index { get; }
        public Source Source { get; }
    
        public string Content
        {
            get { Source...}
            set { Source... }
        }
    }
    

    只会更易读、更优雅,而且……更容易实现。并且更不容易出错,因为更多的细节将是明确的,而不是一些 WPF 的绑定(bind)+附加魔法..

    关于c# - 双向将 "virtual"字符串列表绑定(bind)到列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9710673/

    相关文章:

    c# - 即使鼠标不移动也会触发 Picturebox mousemove 事件

    c# - 从 ThreadID 获取 ThreadName

    wpf - 如何在 WPF ListView 中包装内容?

    wpf - 响应双击时关闭 WPF 窗口会将双击传输到主窗口

    c# - 无法将类型 'void' 隐式转换为 'bool' Xamarin 警报

    c# - 如果数字不是 double 值,则无法显示最多两位小数值的 double 类型值

    c# - 如何在 C# 中访问属性属性中的类变量?

    c# - 如何在VS2010中打开.ccproj?

    c# - 将按钮添加到数据模板并调用其点击事件

    c# - 要使用 AD B2C 验证 .NET Core 3.0 WPF 桌面客户端,如何使用默认操作系统浏览器?