WPF进行视图/编辑控件的好方法吗? [英] WPF A good way to make a view/edit control?

查看:70
本文介绍了WPF进行视图/编辑控件的好方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这只是一个讨论的问题-在WPF中进行视图/编辑控件的最佳方法是什么?例如。我们有一个实体对象Person,里面有一些道具(名字,姓氏,地址,电话等)。控件的一种表示形式是只读视图。另一个将对该人具有编辑视图。示例:

this is just a question to discuss - what is the best way to make a view/edit control in WPF? E.g. we have an entity object Person, that has some props (name, surname, address, phone etc.). One presentation of the control would be a read-only view. And the other would have the edit view for this same person. Example:

<UserControl x:Name="MyPersonEditor">
    <Grid>
        <Grid x:Name="ViewGrid" Visibility="Visible">
            <TextBlock Text="Name:"/>
            <TextBlock Text="{Binding Person.Name}"/>
            <Button Content="Edit" Click="ButtonEditStart_Click"/>
        </Grid>

        <Grid x:Name="EditGrid" Visibility="Collapsed">
            <TextBlock Text="Name:"/>
            <TextBox Text="{Binding Person.Name}"/>
            <Button Content="Save" Click="ButtonEditEnd_Click"/>
        </Grid>
    </Grid>
</UserControl>

我希望这个想法很明确。我现在看到的两个选项

I hope that the idea is clear. The two options I see right now


  1. 两个具有可见性切换的网格和

  2. 一个没有控件的TabControl标题面板

这只是一个讨论问题-麻烦不多,但我只是想知道是否还有其他可能性,

This is just a discussion question - not much trouble with it, yet I am just wondering if there are any other possibilities and elegant solutions to this.

推荐答案

自动锁定类

我编写了一个具有继承的附加 DoLock属性的 AutomaticLock类。

I wrote an "AutomaticLock" class that has an inherited attached "DoLock" property.

将 DoLock属性设置为true会重新模板化所有TextBoxes ComboBoxes,CheckBoxes等是TextBlocks,不可编辑的CheckBoxes等。我的代码经过设置,以便其他附加属性可以指定要在锁定(视图)模式下使用的任意模板,永远不会自动锁定的控件等。

Setting the "DoLock" property to true re-templates all TextBoxes ComboBoxes, CheckBoxes, etc to be TextBlocks, non-editable CheckBoxes,etc. My code is set up so that other attached property can specify arbitrary template to use in locked ("view") mode, controls that should never automatically lock, etc.

因此同一视图可轻松用于编辑和查看。设置单个属性可以对其进行来回更改,并且可以完全自定义,因为视图中的任何控件都可以触发 DoLock属性以任意方式更改其外观或行为。

Thus the same view can easily be used for both editing and viewing. Setting a single property changes it back and forth, and it is completely customizable because any control in the view can trigger on the "DoLock" property to change its appearance or behavior in arbitrary ways.

实施代码

以下是代码:

public class AutomaticLock : DependencyObject
{
  Control _target;
  ControlTemplate _originalTemplate;

  // AutomaticLock.Enabled:  Set true on individual controls to enable locking functionality on that control
  public static bool GetEnabled(DependencyObject obj) { return (bool)obj.GetValue(EnabledProperty); }
  public static void SetEnabled(DependencyObject obj, bool value) { obj.SetValue(EnabledProperty, value); }
  public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(AutomaticLock), new FrameworkPropertyMetadata
  {
    PropertyChangedCallback = OnLockingStateChanged,
  });

  // AutomaticLock.LockTemplate:  Set to a custom ControlTemplate to be used when control is locked
  public static ControlTemplate GetLockTemplate(DependencyObject obj) { return (ControlTemplate)obj.GetValue(LockTemplateProperty); }
  public static void SetLockTemplate(DependencyObject obj, ControlTemplate value) { obj.SetValue(LockTemplateProperty, value); }
  public static readonly DependencyProperty LockTemplateProperty = DependencyProperty.RegisterAttached("LockTemplate", typeof(ControlTemplate), typeof(AutomaticLock), new FrameworkPropertyMetadata
  {
    PropertyChangedCallback = OnLockingStateChanged,
  });

  // AutomaticLock.DoLock:  Set on container to cause all children with AutomaticLock.Enabled to lock
  public static bool GetDoLock(DependencyObject obj) { return (bool)obj.GetValue(DoLockProperty); }
  public static void SetDoLock(DependencyObject obj, bool value) { obj.SetValue(DoLockProperty, value); }
  public static readonly DependencyProperty DoLockProperty = DependencyProperty.RegisterAttached("DoLock", typeof(bool), typeof(ControlTemplate), new FrameworkPropertyMetadata
  {
    Inherits = true,
    PropertyChangedCallback = OnLockingStateChanged,
  });

  // CurrentLock:  Used internally to maintain lock state
  [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  public static AutomaticLock GetCurrentLock(DependencyObject obj) { return (AutomaticLock)obj.GetValue(CurrentLockProperty); }
  public static void SetCurrentLock(DependencyObject obj, AutomaticLock value) { obj.SetValue(CurrentLockProperty, value); }
  public static readonly DependencyProperty CurrentLockProperty = DependencyProperty.RegisterAttached("CurrentLock", typeof(AutomaticLock), typeof(AutomaticLock));


  static void OnLockingStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  {
    AutomaticLock current = GetCurrentLock(obj);
    bool shouldLock = GetDoLock(obj) && (GetEnabled(obj) || GetLockTemplate(obj)!=null);
    if(shouldLock && current==null)
    {
      if(!(obj is Control)) throw new InvalidOperationException("AutomaticLock can only be used on objects derived from Control");
      new AutomaticLock((Control)obj).Attach();
    }
    else if(!shouldLock && current!=null)
      current.Detach();
  }

  AutomaticLock(Control target)
  {
    _target = target;
  }

  void Attach()
  {
    _originalTemplate = _target.Template;
    _target.Template = GetLockTemplate(_target) ?? SelectDefaultLockTemplate();
    SetCurrentLock(_target, this);
  }

  void Detach()
  {
    _target.Template = _originalTemplate;
    _originalTemplate = null;
    SetCurrentLock(_target, null);
  }

  ControlTemplate SelectDefaultLockTemplate()
  {
    for(Type type = _target.GetType(); type!=typeof(object); type = type.BaseType)
    {
      ControlTemplate result =
        _target.TryFindResource(new ComponentResourceKey(type, "AutomaticLockTemplate")) as ControlTemplate ??
        _target.TryFindResource(new ComponentResourceKey(typeof(AutomaticLock), type.Name)) as ControlTemplate;
      if(result!=null) return result;
    }
    return null;
  }
}

此代码将允许您指定自动锁定模板(逐个控件),或者它允许您使用以下视图中定义的默认模板:在包含AutomaticLock类的程序集中,在包含锁模板适用于您的自定义控件的程序集中,在可视树的本地资源中定义的默认模板(包括您的应用程序资源)

This code will allow you to specify an automatic lock template on a control-by-control basis or it will allow you to use default templates defined either in the assembly containing the AutomaticLock class, in the assembly containing your custom control that the lock template applies to, in your local resources in your visual tree (including your application resources)

如何定义AutomaticLock模板

默认模板在合并到Themes / Generic.xaml中的ResourceDictionary中,在包含AutomaticLock类的程序集中定义 WPF标准控件。例如,此模板在锁定时使所有TextBoxes变成TextBlocks:

Default templates for WPF standard controls are defined in the assembly containing the AutomaticLock class in a ResourceDictionary merged into Themes/Generic.xaml. For example, this template causes all TextBoxes to turn into TextBlocks when locked:

<ControlTemplate TargetType="{x:Type TextBox}"
  x:Key="{ComponentResourceKey ResourceId=TextBox, TypeInTargetAssembly={x:Type lc:AutomaticLock}}">
  <TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>

自定义控件的默认模板可以在包含自定义控件的程序集中定义 >在ResourceDictionary中陷入其Themes / Generic.xaml。在这种情况下,ComponentResourceKey是不同的,例如:

Default templates for custom controls may be defined in the assembly containing the custom control in a ResourceDictionary mered into its Themes/Generic.xaml. The ComponentResourceKey is different in this case, for example:

<ControlTemplate TargetType="{x:Type prefix:MyType}"
  x:Key="{ComponentResourceKey ResourceId=AutomaticLockTemplate, TypeInTargetAssembly={x:Type prefix:MyType}}">
    ...

如果应用程序要覆盖特定类型的标准AutomaticLock模板,它可以将自动锁定模板放置在其App.xaml,Window XAML,UserControl XAML或单个控件的ResourceDictionary中。在每种情况下,都应使用与自定义控件相同的方式指定ComponentResourceKey:

If an application wants to override the standard AutomaticLock template for a specific type, it can place an automatic lock template in its App.xaml, Window XAML, UserControl XAML, or in the ResourceDictionary of an individual control. In each case the ComponentResourceKey should be specified the same way as for custom controls:

x:Key="{ComponentResourceKey ResourceId=AutomaticLockTemplate, TypeInTargetAssembly={x:Type prefix:MyType}}"

最后,自动锁定模板可以是通过设置其 AutomaticLock.LockTemplate 属性应用于单个控件。

Lastly, an automatic lock template can be applied to a single control by setting its AutomaticLock.LockTemplate property.

如何在您的控件中使用AutomaticLock UI

要使用自动锁定:


  1. 设置自动锁定。在应自动锁定的任何控件上为Enabled = True。这可以通过样式或直接在单个控件上完成。

  2. 要锁定时,请在顶级控件(窗口,窗口,视图,UserControl等),只要您希望实际发生自动锁定即可。您可以将AutomaticLock.DoLock绑定到复选框或菜单项,也可以通过代码对其进行控制。

关于有效地在视图和编辑模式之间切换

此AutomaticLock类非常适合在视图和编辑模式之间进行切换,即使它们之间存在很大差异。我有几种不同的技术来构造我的视图,以适应编辑时的布局差异。其中一些是:

This AutomaticLock class is great for switching betwen view and edit modes even if they are significantly different. I have several different techniques for constructing my views to accomodate layout differences while editing. Some of them are:


  1. 通过将其Template或AutomaticLockTemplate设置为一个空模板,使控件在编辑或查看模式期间不可见。可能是这样。例如,假设年龄在视图模式下位于布局的顶部,而在编辑模式下位于布局的底部。在两个地方都为年龄添加一个文本框。在顶部,将模板设置为空模板,这样它就不会在编辑模式下显示。在底部,将AutomaticLockTemplate设置为空模板。现在一次只能看到一个。

  1. Make controls invisible during edit or view mode by setting either their Template or AutomaticLockTemplate to an empty template as the case may be. For example, suppose "Age" is at the top of your layout in view mode and at the bottom in edit mode. Add a TextBox for "Age" in both places. In the top one set Template to the empty template so it doesn't show in Edit mode. In the bottom one set AutomaticLockTemplate to the empty template. Now only one will be visible at a time.

使用ContentControl替换内容周围的边框,布局面板,按钮等,而不会影响内容。 ContentControl的模板具有周围的边框,面板,按钮等用于编辑模式。它还有一个具有视图模式版本的AutomaticLockTemplate。

Use a ContentControl to replace borders, layout panels, buttons, etc surrounding content without affecting the content. The ContentControl's Template has the surrounding borders, panels, buttons, etc for edit mode. It also has an AutomaticLockTemplate that has the view mode version.

使用控件替换视图的矩形部分。 (这实际上是指控件类的对象,而不是therof的子类。)同样,您将编辑模式版本放入模板中,并将视图模式版本放入自动锁定模板。

Use a Control to replace a rectangular section of your view. (By this I actually mean an object of class "Control", not a subclass therof.) Again, you put your edit mode version in the Template and your view mode version in the AutomaticLockTemplate.

使用带有额外自动大小的行和列的网格。在AutomaticLock.DoLock属性上使用触发器,以更新Grid中项目的Row,Column,RowSpan和ColumnSpan属性。例如,您可以通过将Grid.Row从6更改为0,将包含年龄控件的面板移到顶部。

Use a Grid with extra Auto-sized rows and columns. Use a trigger on the AutomaticLock.DoLock property to update the Row, Column, RowSpan, and ColumnSpan properties of the items within the Grid. For example you could move a panel containing an "Age" control to the top by changing its Grid.Row from 6 to 0.

在DoLock上触发以应用一个LayoutTranform或RenderTransform到您的项目,或设置其他属性,例如Width和Height。如果您希望在编辑模式下使事情变大,或者想要使TextBox变宽并将其旁边的按钮移到边缘上方,则此功能很有用。

Trigger on DoLock to apply a LayoutTranform or RenderTransform to your items, or to set other properties like Width and Height. This is useful if you want things to be bigger in edit mode, or if you want to make a TextBox wider and move the button beside it over against the edge.

请注意,您可以在整个视图中使用选项#3(带有用于编辑和视图模式的单独模板的Control对象)。如果编辑和查看模式完全不同,则可以这样做。在这种情况下,AutomaticLock仍然为您提供了手动设置两个模板的便利。看起来像这样:

Note that you can use option #3 (a Control object with separate templates for edit and view modes) for the entire view. This would be done if the edit and view modes were completely different. In this case AutomaticLock still gives you the convenience of being able to set the two templates manually. It would look like this:

<Control>
  <Control.Template>
    <ControlTemplate>
      <!-- Edit mode view here -->
    </ControlTemplate>
  </Control.Template>
  <lib:AutomaticLock.LockTemplate>
    <ControlTemplate>
      <!-- View mode view here -->
    </ControlTemplate>
  </lib:AutomaticLock.LockTemplate>
</Control>

通常,在编辑和查看模式之间更容易调整一些小位置和东西,并且更好为用户提供体验,因为用户将具有一致的布局,但是如果您需要完整的替代品,AutomaticLock也可以为您提供这种功能。

Generally it is easier to tweak a few little positions and things between the edit and view modes, and better for your user experience because the user will have consistent layout, but if you do need a complete replacement AutomaticLock gives you that power as well.

这篇关于WPF进行视图/编辑控件的好方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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