自定义我的控件的一部分

Customize parts of my control

我正在开发通用 window SDK。

SDK 的主要入口点是派生的 UserControl class,它公开了一些功能。

我们不允许用户完全改变外观,这就是我不喜欢 CustomControl + generic.xaml + 模板部件方法的原因。

但是,我们希望允许自定义一些子项。就像我们控制的三个不同按钮一样。我想提供合理的默认值 styles/templates/content,但我希望我的用户能够完全重新设计这些子项,以备不时之需。或者,只是改变例如背景颜色,如果他们想这样做的话。

最好的方法是什么?

  1. 公开我的控件的(依赖项?)属性 Style Button1Style ?
  2. 公开我的控件的(依赖项?)属性 ControlTemplate Button1Template ?
  3. 公开我的控件的(依赖项?)属性 DataTemplate Button1ContentTemplate ?
  4. 这些的组合?
  5. 其他方式?

SDK 是可移植的,需要支持 Windows 8.1 和 10,Store 和 Phones。

更新:我知道数据绑定工作需要依赖属性。我不知道我的用户是否会使用数据绑定,或者他们不会。 我不需要动画。

我期望的用例如下:

  1. 大多数客户应该对默认外观感到满意 - 对于他们,我只需要提供合理的默认外观。
  2. 有些人会想要更改某些子控件的颜色或边距。
  3. 有些人会用他们自定义的图标替换我们的图标。
  4. 有些将完全修改我们按钮的样式和模板。

我想让他们在 MS Blend 中进行 2-4 次,仅在 XAML 中进行。怎么样?

@Soonts,这里有很多变数。而且,尽管许多 Whosebug 书呆子对开发人员在一个问题中提出多个问题感到非常生气,但我认为我们可以共同努力以获得您的答案。

首先,我认为你应该回顾一下我关于这个主题的文章。它侧重于用户控件中的双向绑定,诚然,这是一个技巧。

http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html

其次,我认为了解 ControlTemplate 绑定、属性 动画和绑定以及 DataTemplate 范围很重要。我在 Microsoft Virtual Academy 的一门课程中就这个话题讲得很深入。

https://mva.microsoft.com/en-us/training-courses/xaml-for-windows-10-controls-14482

对您的大部分问题的简短回答是,依赖属性正是您所需要的。因为您用 Windows 8 标记了您的问题,所以利用 属性 元数据中定义的已更改事件很重要。同样,这在文章中。

最后,我写了一个重要的用户控件派生控件,所以我可以确认你想要完成的事情是肯定有可能的。如果你想查看我的代码,看看我们是否在做你想窃取的任何东西,你也可以这样做。

https://github.com/Windows-XAML/Template10/blob/master/Template10%20(Library)/Controls/HamburgerMenu.xaml.cs

祝你好运。

已实施 #1,工作正常。

  1. Style/template那些部分,把那些styles/templates放在单独的资源字典里"DefaultStyles".
  2. 创建 code behind for that resource dictionary。这样做是为了避免疯狂的 "ms-appx:///" URI,而是通过 class 名称引用字典。
  3. 在用户控件代码中,为我的控件的可自定义部分添加几个 "Style" 类型的依赖属性。
  4. 在用户控件代码中,在构造函数中,将控件的根元素(但不是控件本身)的数据上下文设置为 this
  5. 在用户控件 XAML 中,包括 DefaultStyles 资源:

    <ResourceDictionary.MergedDictionaries>
        <local:DefaultStyles />
    </ResourceDictionary.MergedDictionaries>
    
  6. 在用户控件 XAML 中,引用样式:<Button Style="{Binding MyButtonStyle, TargetNullValue={StaticResource MyButtonDefaultStyle}}" /> 其中 MyButtonStyle 是在步骤 #3 中创建的依赖项 属性,MyButtonDefaultStyle 是在步骤 #1

  7. 中创建的默认样式
  8. 在客户端应用页面中:

    <ResourceDictionary.MergedDictionaries>
        <sdk:DefaultStyles />
    </ResourceDictionary.MergedDictionaries>
    <!-- Debug code: override a single property -->
    <Style x:Key="sss" TargetType="Button" BasedOn="{StaticResource MyButtonDefaultStyle}">
        <Setter Property="Foreground" Value="Green" />
    </Style>
    
  9. 最后,在客户端应用页面:

    <sdk:TheControl MyButtonStyle={StaticResource sss} />
    

这样,一切都开箱即用,我的用户无需复制粘贴 XAML 页面即可调整单个边距或颜色,或者完全忽略我提供的 styles/templates并创建自己的。

请问有没有更简单的方法?

任务 "Allow user to override styles, partially or completely" 听起来并不难完成。

更新: 由于某些原因,在 windows phone 8.1 中,您无法设置 Content 属性样式(没有错误,只是没有效果)。幸运的是,您可以改为设置 ContentTemplate 属性,这似乎适用于所有平台。

啊,你的问题改了。给你。

这是我过去的做法。

<UserControl>
    <Grid Background="{x:Bind MyBackground, FallbackValue=White}">
        <TextBlock Text="{x:Bind MyText, FallbackValue=DesigntimeValue }" />
    </Grid>
</UserControl>

Please note that {x:Bind} does not support designtime values, so using FallBackValue is basically giving you the designetime value. Don't confuse this with your default value.

那么这个代码隐藏:

public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
    public MyUserControl()
    {
        InitializeComponent();
    }

    Brush _MyBackground = new SolidColorBrush(Colors.Blue);
    public Brush MyBackground { get { return _MyBackground; } set { Set(ref _MyBackground, value); } }

    string _MyText = "Default value";
    public string MyText { get { return _MyText; } set { Set(ref _MyText, value); } }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (!Equals(storage, value))
        {
            storage = value;
            RaisePropertyChanged(propertyName);
        }
    }

    void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    #endregion  
}

This is just how we implement INotifyPropertyChanged in Template 10 (http://aka.ms/template10). You can implement it however you like.

并像这样使用它:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <local:MyUserControl MyBackground="Yellow" MyText="JerryNixon" />
</Grid>

The default values is what you will see in the designtime of your app, not the fallback. FallBackValues will be what you see when designing the user control itself.

值得指出的是,您可以将这些属性中的任何一个替换为依赖属性。您想要这样做的唯一原因是您希望它们具有动画效果。

希望对您有所帮助。祝你好运。