WPF包装控制 [英] Wrapped WPF Control

查看:53
本文介绍了WPF包装控制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个GUI(WPF)库,其中的每个(自定义)控件基本上都包装了一个内部(第三方)控件。然后,我要手动公开每个属性(不是所有属性,而是几乎所有属性)。在XAML中,结果控件非常简单:

 < my:CustomButton Content = ClickMe /> 

后面的代码也很简单:

 公共类CustomButton:Control 
{
private MyThirdPartyButton _button = null;

静态CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton),new FrameworkPropertyMetadata(typeof(CustomButton))));
}

public CustomButton()
{
_button = new MyThirdPartyButton();
this.AddVisualChild(_button);
}

受保护的重写int VisualChildrenCount
{
get
{return _button == null吗? 0:1; }
}

受保护的重写Visual GetVisualChild(int index)
{
if(_button == null)
{
throw new ArgumentOutOfRangeException ();
}
return _button;
}

#region属性:内容
公共对象内容
{
get {return GetValue(ContentProperty); }
set {SetValue(ContentProperty,value); }
}

公共静态只读DependencyProperty ContentProperty = DependencyProperty.Register(
Content,typeof(Object),
typeof(CustomButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent))
);

私有静态void ChangeContent(DependencyObject源,DependencyPropertyChangedEventArgs e)
{
(作为CustomButton来源).UpdateContent(e.NewValue);
}

private void UpdateContent(Object sel)
{
_button.Content = sel;
}
#endregion
}

问题出在我们之后将MyThirdPartyButton公开为属性(以防万一我们不公开某些内容,我们希望为程序员提供直接使用它的方法)。通过简单地创建属性,像这样:

  public MyThirdPartyButton InternalControl 
{
get {return _button; }
set
{
if(_button!= value)
{
this.RemoveVisualChild(_button);
_button =值;
this.AddVisualChild(_button);
}
}
}

生成的XAML就是这样:

 < my:CustomButton> 
< my:CustomButton.InternalControl>
< thirdparty:MyThirdPartyButton Content = ClickMe />
< / my:CustomButton.InternalControl>



我在找什么例如,

 < my:CustomButton> 
< my:CustomButton.InternalControl Content = ClickMe />



但是(用代码I拥有)无法向InternalControl添加属性...



有任何想法/建议吗?



非常感谢,



-
Robert

解决方案

WPF的动画系统可以设置对象的子属性,但是XAML解析器不能。



两种解决方法:


  1. 在InternalControl属性设置器中,获取传入的值并通过其DependencyProperties进行迭代,将其复制到实际的InternalControl中。

  2. 使用构建事件以编程方式创建

我将依次解释每个属性。



使用属性设置器设置属性



此解决方案不会产生您想要的简化语法,但是很简单实施并可能y解决的主要问题是,如何将容器控件上设置的值与内部控件上设置的值合并。



对于此解决方案,您将继续使用XAML,不喜欢:

 < my:CustomButton Something = Abc> 
< my:CustomButton.InternalControl>
< thirdparty:MyThirdPartyButton Content = ClickMe />
< / my:CustomButton.InternalControl>

但是您实际上并没有最终更换InternalControl。



为此,您的InternalControl的设置器将是:

  public InternalControl InternalControl 
{
get {return _internalControl; }
set
{
var enumerator = value.GetLocalValueEnumerator();
while(enumerator.MoveNext())
{
var entry = enumerator.Current as LocalValueEntry;
_internalControl.SetValue(entry.Property,entry.Value);
}
}
}

您可能需要一些其他逻辑排除不公开可见或默认设置的DP。实际上,可以通过在静态构造函数中创建虚拟对象并创建默认情况下具有局部值的DP列表来轻松解决此问题。



使用构建事件创建附加属性



此解决方案允许您编写非常漂亮的XAML:

 < my:CustomButton Something = Abc 
my:ThirdPartyButtonProperty.Content = ClickMe />

实现是在构建事件中自动创建ThirdPartyButtonProperty类。 build事件将使用CodeDOM为在ThirdButton中声明的每个属性(尚未在CustomButton中镜像)构造附加属性。在每种情况下,附加属性的PropertyChangedCallback都会将值复制到InternalControl的相应属性中:

 公共类ThirdPartyButtonProperty 
{
公共静态对象GetContent(...
公共静态void SetContent(...
公共静态只读DependencyProperty ContentProperty = DependencyProperty.RegisterAttached( Content,typeof(object), typeof(ThirdPartyButtonProperty),新的PropertyMetadata
{
PropertyChangedCallback =(obj,e)=>
{
((CustomButton)obj).InternalControl.Content =(object)e .NewValue;
}
});
}

此实现的一部分很简单:棘手的部分是创建MSBuild任务,从.csproj引用它,并对它进行排序,以便它在my:CustomButton的预编译之后运行,以便可以看到它需要添加哪些附加属性。 / p>

I'm trying to create a GUI (WPF) Library where each (custom) control basically wraps an internal (third party) control. Then, I'm manually exposing each property (not all of them, but almost). In XAML the resulting control is pretty straightforward:

<my:CustomButton Content="ClickMe" />

And the code behind is quite simple as well:

public class CustomButton : Control
{    
    private MyThirdPartyButton _button = null;

    static CustomButton()
    {        
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
    }

    public CustomButton()
    {
        _button = new MyThirdPartyButton();
        this.AddVisualChild(_button);        
    }

    protected override int VisualChildrenCount
    {    
        get
            { return _button == null ? 0 : 1; }
    }

    protected override Visual GetVisualChild(int index)
    {
        if (_button == null)
        {
            throw new ArgumentOutOfRangeException();
        }
        return _button;
    }

    #region Property: Content
    public Object Content
    {
        get { return GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }
    }

    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
                        "Content", typeof(Object),
                    typeof(CustomButton), 
                        new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent))
    );

    private static void ChangeContent(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        (source as CustomButton).UpdateContent(e.NewValue);
    }

    private void UpdateContent(Object sel)
    {
        _button.Content = sel;
    }
    #endregion
}

The problem comes after we expose MyThirdPartyButton as a property (in case we don't expose something, we would like to give the programmer the means to use it directly). By simply creating the property, like this:

public MyThirdPartyButton InternalControl
{
    get { return _button; }
    set
    {
        if (_button != value)
        {
            this.RemoveVisualChild(_button);
            _button = value;
            this.AddVisualChild(_button);
        }
    }
}

The resulting XAML would be this:

<my:CustomButton>
<my:CustomButton.InternalControl>
    <thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>

And what I'm looking for, is something like this:

<my:CustomButton>
<my:CustomButton.InternalControl Content="ClickMe" />

But (with the code I have) its impossible to add attributes to InternalControl...

Any ideas/suggestions?

Thanks a lot,

-- Robert

解决方案

WPF's animation system has the ability to set sub-properties of objects, but the XAML parser does not.

Two workarounds:

  1. In the InternalControl property setter, take the value passed in and iterate through its DependencyProperties copying them to your actual InternalControl.
  2. Use a build event to programmatically create attached properties for all internal control properties.

I'll explain each of these in turn.

Setting properties using the property setter

This solution will not result in the simplified syntax you desire, but it is simple to implement and will probably solve the main problem with is, how to merge values set on your container control with values set on the internal control.

For this solution you continue to use the XAML you didn't like:

<my:CustomButton Something="Abc">
  <my:CustomButton.InternalControl> 
    <thirdparty:MyThirdPartyButton Content="ClickMe" /> 
  </my:CustomButton.InternalControl> 

but you don't actually end up replacing your InternalControl.

To do this, your InternalControl's setter would be:

public InternalControl InternalControl
{
  get { return _internalControl; }
  set
  {
    var enumerator = value.GetLocalValueEnumerator();
    while(enumerator.MoveNext())
    {
      var entry = enumerator.Current as LocalValueEntry;
      _internalControl.SetValue(entry.Property, entry.Value);
    }
  }
}

You may need some additional logic to exclude DPs not publically visible or that are set by default. This can actually be handled easily by creating a dummy object in the static constructor and making a list of DPs that have local values by default.

Using a build event to create attached properties

This solution allows you to write very pretty XAML:

<my:CustomButton Something="Abc"
                 my:ThirdPartyButtonProperty.Content="ClickMe" />

The implementation is to automatically create the ThirdPartyButtonProperty class in a build event. The build event will use CodeDOM to construct attached properties for each property declared in ThirdPartyButton that isn't already mirrored in CustomButton. In each case, the PropertyChangedCallback for the attached property will copy the value into the corresponding property of InternalControl:

 public class ThirdPartyButtonProperty
 {
   public static object GetContent(...
   public static void SetContent(...
   public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), typeof(ThirdPartyButtonProperty), new PropertyMetadata
   {
     PropertyChangedCallback = (obj, e) =>
     {
       ((CustomButton)obj).InternalControl.Content = (object)e.NewValue;
     }
   });
 }

This part of the implementation is straightforward: The tricky part is creating the MSBuild task, referencing it from your .csproj, and sequencing it so that it runs after the precompile of my:CustomButton so it can see what additional properties it needs to add.

这篇关于WPF包装控制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆