WPF进行 View /编辑控件的好方法吗?

标签 wpf controls crud

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

<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>

我希望这个想法很明确。我现在看到的两个选项
  • 具有可见性切换和
  • 的两个网格
  • 一个没有标题面板的TabControl

  • 这只是一个讨论问题,没有太多麻烦,但我只是想知道是否还有其他可能性和优雅的解决方案。

    最佳答案

    自动锁定类

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

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

    因此,同一 View 可以轻松地用于编辑和查看。设置单个属性来回更改它,并且它是完全可定制的,因为 View 中的任何控件都可以触发“DoLock”属性来以任意方式更改其外观或行为。

    实现代码

    这是代码:

    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类的程序集中,在包含您的自定义控件的程序集中定义的默认模板所定义的锁模板到您在视觉树中的本地资源(包括您的应用程序资源)中

    如何定义AutomaticLock模板

    WPF标准控件的默认模板在包含在ResourceDictionary中的AutomaticLock类的程序集中定义,该组件合并到Themes/Generic.xaml中。例如,此模板使所有TextBoxes在锁定时都变成TextBlocks:
    <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是不同的,例如:
    <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:
    x:Key="{ComponentResourceKey ResourceId=AutomaticLockTemplate, TypeInTargetAssembly={x:Type prefix:MyType}}"
    

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

    如何在用户界面中使用AutomaticLock

    要使用自动锁定:
  • 在应自动锁定的所有控件上设置AutomaticLock.Enabled =“True”。这可以通过样式或直接在单个控件上完成。它启用了控件上的锁定,但不会导致控件实际锁定。
  • 当您想要锁定时,只要您希望实际发生自动锁定,就在顶级控件(窗口, View ,UserControl等)上设置AutomaticLock.DoLock =“True”。您可以将AutomaticLock.DoLock绑定(bind)到复选框或菜单项,也可以通过代码对其进行控制。

  • 有关在查看和编辑模式之间有效切换的一些技巧

    即使 View 和编辑模式有很大不同,此AutomaticLock类也非常适用于它们之间的切换。我有几种不同的技术来构造 View ,以适应编辑时的布局差异。他们之中有一些是:
  • 通过视情况将控件的模板或自动锁模板设置为空模板,使控件在编辑或查看模式下不可见。例如,假设“年龄”在 View 模式下位于布局的顶部,而在编辑模式下位于布局的底部。在两个地方都为“年龄”添加一个文本框。在顶部,将"template"设置为空模板,这样它就不会在“编辑”模式下显示。在底部,将AutomaticLockTemplate设置为空模板。现在一次只能看到一个。
  • 使用ContentControl替换内容周围的边框,布局面板,按钮等,而不会影响内容。 ContentControl的模板具有用于编辑模式的周围边框,面板,按钮等。它还具有一个具有 View 模式版本的AutomaticLockTemplate。
  • 使用控件替换 View 的矩形部分。 (实际上,这是指“控件”类的对象,而不是therof的子类。)同样,您将编辑模式版本放入"template"中,并将 View 模式版本放入“自动锁定模板”中。
  • 使用带有额外自动大小的行和列的网格。在AutomaticLock.DoLock属性上使用触发器,以更新Grid中项目的Row,Column,RowSpan和ColumnSpan属性。例如,您可以通过将其“Grid.Row”从6更改为0,将包含“年龄”控件的面板移至顶部。
  • 在DoLock上触发以将LayoutTranform或RenderTransform应用于您的项目,或设置其他属性(例如Width和Height)。如果您希望在编辑模式下将内容变大,或者想要使TextBox变宽并将其旁边的按钮移到边缘上方,则此功能很有用。

  • 请注意,您可以对整个 View 使用选项#3(带有用于编辑和 View 模式的单独模板的Control对象)。如果编辑和查看模式完全不同,则可以执行此操作。在这种情况下,AutomaticLock仍然为您提供了手动设置两个模板的便利。它看起来像这样:
    <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也可以为您提供这种功能。

    关于WPF进行 View /编辑控件的好方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3042869/

    相关文章:

    silverlight - wp7中如何让按钮内容显示在左侧?

    PostgreSQL : Update multiple inner jsonb objects fields with same name

    wcf - 使用WCF跳入N层架构吗?

    c# - WPF - UI 未从命令更新

    wpf - 使用 Prism 时将应用程序的部分拆分为各自的程序集

    c# - 我应该创建什么而不是 Bootstrap ? Prism 6.1.0

    delphi - 如何以编程方式将组件分配给Delphi GridPanel行和列?

    jsp - IDE 或 JSP 中的 CRUD 实用程序

    wpf - 在WPF MVVM中使用行为?