【问题标题】:WPF Binding in Custom Control自定义控件中的 WPF 绑定
【发布时间】:2014-03-16 12:07:24
【问题描述】:

我在 WPF 中有一个 CustomControl。

[TemplatePart(Name = ControlSaveButtonName, Type = typeof(Button))]
[TemplatePart(Name = ControlCancelButtonName, Type = typeof(Button))]
[TemplatePart(Name = ControlPanelName, Type = typeof(Panel))]
public class BaseEditEntityControl : Control
{
    public const string ControlSaveButtonName = "PART_SaveButton";
    public const string ControlCancelButtonName = "PART_CancelButton";
    public const string ControlPanelName = "PART_Panel";

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

    private Button _saveButtonControl;
    public Button SaveButtonControl
    {
        get { return _saveButtonControl; }
    }
    private Button _cancelButtonControl;
    public Button CancelButtonControl
    {
        get { return _cancelButtonControl; }
    }
    private Panel _panelControl;
    public Panel PanelControl
    {
        get { return _panelControl; }
    }

    public static readonly DependencyProperty EntityProperty = DependencyProperty.Register(
        "Entity", typeof(object), typeof(BaseEditEntityControl));
            //, new FrameworkPropertyMetadata
            //{
            //    BindsTwoWayByDefault = true,
            //    PropertyChangedCallback = BaseEntityPropertyChanged
            //});

    public object Entity
    {
        get { return GetValue(EntityProperty); }
        set { SetValue(EntityProperty, value); FillPropertyControls(); }
    }

    public BaseEditEntityControl()
    {
    }

    public override void OnApplyTemplate()
    {
         base.OnApplyTemplate();

        _saveButtonControl = GetTemplateChild(ControlSaveButtonName) as Button;
        _cancelButtonControl = GetTemplateChild(ControlCancelButtonName) as Button;
        _panelControl = GetTemplateChild(ControlPanelName) as Panel;

        if (_saveButtonControl != null) _saveButtonControl.Click += SaveButton_Click;
        if (_cancelButtonControl != null) _cancelButtonControl.Click += CancelButton_Click;
    }

    public void FillPropertyControls()
    { 
        //Some Code
    }

    //private static void BaseEntityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    //{
    //    var baseEntityControl = d as BaseEditEntityControl;
    //    if (baseEntityControl == null) return;
    //
    //    baseEntityControl.Entity = e.NewValue as BaseEntity;
    //}
}

控制模板:

<Style x:Key="PassportEntityEditControlStyle" TargetType="{x:Type base:BaseEditEntityControl}">
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type base:BaseEditEntityControl}">
                <StackPanel x:Name="PART_Panel" Orientation="Vertical">
                    <TextBlock Text="{Binding Entity, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=base:BaseEditEntityControl}}"></TextBlock>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

控制使用:

<Style TargetType="propertyControls1:PersonPassportPropertyControl">
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="propertyControls1:PersonPassportPropertyControl">
                <Grid ScrollViewer.CanContentScroll="True">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                <base:BaseEditEntityControl x:Name="PART_Value" ScrollViewer.CanContentScroll="True"
                      Entity="{Binding PropertyBinding, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=propertyControls1:PersonPassportPropertyControl}}" 
                      Style="{StaticResource PassportEntityEditControlStyle}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

PersonPassportPropertyControl 也是一个自定义控件。

问题:文本框已填充,但未调用“实体集”和 FillPropertyControls。如果取消注释 PropertyChangedCallback 则调用 BaseEntityPropertyChanged。

【问题讨论】:

  • 您的观察是正确的,依赖属性既不调用setter,也不调用getter。这就是为什么您的注释代码是正确的。 setter/getter 中唯一有效的代码是 SetValue/GetValue。

标签: c# .net wpf binding custom-controls


【解决方案1】:

绑定不使用 get 和 set 访问器。

通过以下方式更改实体属性:

    //Entity Dependency Property
    public object Entity
    {
        get { return (object)GetValue(EntityProperty); }
        set { SetValue(EntityProperty, value); }
    }
    public static readonly DependencyProperty EntityProperty =
        DependencyProperty.Register("Entity", typeof(object), 
        typeof(BaseEditEntityControl),
        new UIPropertyMetadata(null),
        (d, e) => { FillPropertyControls(); });

【讨论】:

    【解决方案2】:

    这就是它的设计方式。文档中也明确提到 XAML 绕过属性包装器并直接调用 GetValue 和 SetValue。

    来自MSDN:

    其 XAML 处理器的当前 WPF 实现本质上是 依赖属性感知。 WPF XAML 处理器使用属性系统 加载二进制 XAML 时依赖属性的方法和 处理作为依赖属性的属性。 这有效 绕过属性包装器。当您实现自定义依赖项时 属性,您必须考虑这种行为并应避免 将任何其他代码放在您的属性包装器中,而不是 属性系统方法 GetValue 和 SetValue。

    使用 PropertyChangeCallback 代替放置您想要在依赖属性更改时执行的代码。

    【讨论】:

      猜你喜欢
      • 2015-06-03
      • 2013-03-15
      • 1970-01-01
      • 2011-08-06
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多