Styling/binding 当 FrameworkElement 是 ContentControl 的 属性

Styling/binding when a FrameworkElement is a property for a ContentControl

我从一个非常复杂的实际项目创建了一个较小的测试项目,但想法是一样的。两者都有同样的问题。

我有两个 class,A 和 B。A 是一种 ContentControl,B 是一种 FrameworkElement。 A 有一个依赖项 属性 Bee(B 的类型)。 B 具有依赖性 属性 文本(字符串类型)。 A 的默认值为 null,B 的默认值为 "B There!".

我在测试中使用样式 window (Window.Resources) 将 B 的文本 属性 设置为默认值以外的值。

<Window.Resources>
    <Style TargetType="local:B">
        <Setter Property="Text" Value="YO!!!"/>
    </Style>
</Window.Resources>

当我将 A.Content 设置为 B 的新实例时,一切都很顺利,因为系统会处理一切。 IE。当我在 Visual Studio 的设计器中更改样式 setter 的值时,它会更改相应的 属性 值 (B.Text)。数据绑定也可以正常工作。

<local:A.Content>
    <local:B/>
</local:A.Content>

我真正需要的是下面...

由于我无法控制的原因,我不能为此使用 A.Content,因此我创建了一个新的 B 实例并将其设置为 A.Bee 属性。在这种情况下,隐式样式和数据上下文继承不起作用。 IE。当我更改样式 setter 的值时,没有任何反应,数据绑定也不起作用。

<local:A.Bee>
    <local:B/>
</local:A.Bee>

在 A.Bee 属性 值更改回调中,我使用 AddLogicalChild、AddVisualChild 和数据绑定将 B 实例放入树中并使数据绑定工作。 IE。当设置 A.Bee 时,会发生以下情况(简化)...

private static void BeeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    A instanceOfA = d as A;
    B newValue = e.NewValue as B;

    Binding b = new Binding("DataContext");
    b.Source = instanceOfA;

    BindingOperations.SetBinding(newValue, B.DataContextProperty, b);

    instanceOfA.AddLogicalChild(newValue);
    instanceOfA.AddVisualChild(newValue);
}

这解决了数据绑定问题。不确定这是否是正确的方法,但它确实有效。让我知道是否有更好的方法。但是,样式在 Visual Studio 的设计器中仍然不起作用。

如何绑定样式值,以便在 Visual Studio 的设计器中更改样式 setter 的值时 属性 值发生变化?

在使用 AddLogicalChild 之前,newValue 在其资源中没有样式,所以我认为我在正确的轨道上,但还不完全正确,但是...

如有必要,如果有人想要test/modify,我可以在我的项目中添加一个link。


这里有一个 link 的测试解决方案 (Visual Studio 2013): FEStyleTesting


简而言之,我想使用 B class 的实例设置 A.Bee 属性 就像设置 A.Content 属性 一样B class 的一个实例。 IE。使用 Visual Studio 设计器时,数据上下文和样式继承到 B class 的实例。


在图中,我正在调试设计器,如您所见,调用 AddLogicalChild 后可以从资源中找到样式 setters,但由于某种原因,该样式未在设计器中应用...在设计器中,InstantietedElementViewNode 属性 有第三个 setter。 IE。它在 运行 时间内不存在。也许这是设计者为每个控件添加的东西...前两个 setter 是 XAML 中定义的(忘记将它们扩展为图片)。


澄清一下,在测试应用程序中,想法是使用消息框进行测试。 IE。我不需要改变视觉外观 运行ce(使用内容 属性 时它会改变)。只需在 XAML.

中更改样式 setter 值时调用回调

以下是测试此问题的步骤:

  1. 打开解决方案。
  2. 确保没有打开任何源标签页。
  3. 重建解决方案以防万一。
  4. 打开 MainWindow.xaml(双击它)。
  5. 如果出现消息框,请单击 "Ok" 按钮。
  6. 确保"These don't work"下的第一个赋值没有被注释,其他三个被注释掉。 (由于某些原因无法在此列表中获取源代码...)
  7. 现在将文本 属性 的样式 setter 的值更改为例如"YO2!!!".
  8. 一个 "TextChanged" 消息框应该在编辑值后出现(但它没有)。

B 是有意派生自 ContentControl(而不是 FrameworkElement)的,这样我就可以在使用 A.Content 属性 时验证数据绑定和样式继承。 ContentControl 派生自 FrameworkElement,因此结果应该相同。 FrameworkElement 在我的大项目中使用。


在调试设计器时,我将 "YO!!!" 更改为 "YO2!!!",然后在 XAML 中显式更改 B.Color。然后在 ColorChanged 回调中,我查看了 B 实例的资源并注意到该值已相应更改。因此,当我在 XAML 中更改它们时样式值会更新,但由于某种原因它们不会更改 属性 值(文本仍然是 "YO!!!")...因此,没有调用任何回调。

我想知道是什么原因造成的...


我可能找到了可能的解决方案。我注意到即使我调用了 AddLogicalChild,A.LogicalChildren 也没有包含 B 对象。然后我通过可覆盖的方法和 运行 进入可覆盖的 LogicalChildren 属性.

这是我的版本:

protected override IEnumerator LogicalChildren
{
    get
    {
        List<object> list = new List<object>();

        while (base.LogicalChildren.MoveNext() == true)
        {
            try
            {
                list.Add(base.LogicalChildren.Current);
            }
            catch
            {
                // If Current fails, break.
                break;
            }
        }

        list.Add(Bee);

        return list.GetEnumerator();
    }
}

不确定这是否是 "ultimate" 解决方案,但现在每次更改样式 setter 值时都会调用我的 属性 更改回调。好吧,生活和学习......最好再测试一下。

所以问题的答案是覆盖 LogicalChildren 属性 以包含嵌套对象。这也将消除 BindingOperations 绑定到 DataContext 的需要。 AddLogicalChild 仍然是必需的。不确定 AddVisualChild 但安全总比后悔好...