如何仅修改WPF控件的Margin属性的右侧(或左侧、顶部、底部)值?

18

从代码后台实现这个很容易:

var button = new Button();
var margin = button.Margin;
margin.Right = 10;
button.Margin = margin;

然而,在XAML中,我只能使用以下内容:

<Button Margin="0,0,10,0" />
这个问题的困境在于我有可能通过将其他边距值(比如左,上,下)设为零来覆盖它们。是否有办法让XAML像下面这样呢?
<Button MarginRight="10" />
4个回答

17

可以使用附加属性。事实上,这正是附加属性的目的:访问父元素属性或为特定元素添加额外功能。

例如,在应用程序的某个地方定义以下类:

using System;
using System.Windows;
using System.Windows.Controls;

namespace YourApp.AttachedProperties
{
    public class MoreProps
    {
        public static readonly DependencyProperty MarginRightProperty = DependencyProperty.RegisterAttached(
            "MarginRight",
            typeof(string),
            typeof(MoreProps),
            new UIPropertyMetadata(OnMarginRightPropertyChanged));

        public static string GetMarginRight(FrameworkElement element)
        {
            return (string)element.GetValue(MarginRightProperty);
        }

        public static void SetMarginRight(FrameworkElement element, string value)
        {
            element.SetValue(MarginRightProperty, value);
        }

        private static void OnMarginRightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var element = obj as FrameworkElement;

            if (element != null)
            {
                int value;
                if (Int32.TryParse((string)args.NewValue, out value))
                {
                    var margin = element.Margin;
                    margin.Right = value;
                    element.Margin = margin;
                }
            }
        }
    }
}

现在你只需在 XAML 中声明以下命名空间:

xmlns:ap="clr-namespace:YourApp.AttachedProperties"

然后你可以编写类似以下的 XAML:

<Button ap:MoreProps.MarginRight="10" />



或者,您可以避免使用附加属性,而是编写稍微冗长一些的XAML代码,例如:

<Button>
    <Button.Margin>
        <Thickness Right="10" />
    </Button.Margin>
</Button>


4
最后的 XAML 代码是指相当于Margin="0,0,10,0",因为它会覆盖已有的 Thickness。请注意,这里的“覆盖”是指替换或重写现有的 Thickness 属性,并非添加到现有属性中。 - H.B.
2
@H.B. 谢谢,我应该测试一下最后一部分。你是对的。整个边距将被重新分配,任何未指定的值都将返回默认值。我已经编辑了答案来标记那一部分。 - bugged87
不幸的是,当附加属性被设置时,边距尚未设置,因此我修改了默认的0边距而不是在XAML中指定的边距。 - Matus

1

尽管附加属性可以工作,但我建议您重构代码,使其不要在代码后台进行UI更改。您应该尽可能在文件的设计侧处理UI。我尽量少使用xaml文件的代码后台,因为它会导致MVVM方面的问题。


1
你有关于重构代码的建议吗?如果XAML设计师缺少所需的功能,那么在代码后台添加额外功能似乎是一个不错的选择。请记住,在单个位置定义和维护的附加属性并不真正等同于在每个XAML文件的代码后台编写该代码。我猜这也取决于你在代码后台做了什么。如果你修改了数据上下文,那么可能会影响MVVM模式。但是,如果你只修改UI组件,那么这可能没问题。 - bugged87
1
这是我一直以来的问题...代码后台是一个带有XAML代码的部分类...它与XAML处于同一级别。如果它只能用于执行UI相关的活动,那么它与在XAML中执行没有什么不同。 - AshbyEngineer

1

将数据绑定到我的视图模型中的属性意味着我的视图模型必须维护一个特定于UI设计的值。这会破坏MVVM模式。 - bugged87
我完全同意你的观点...我们在哪里划定“UI”特定功能的界限,例如根据状态着色行。如果涉及“逻辑”,代码依赖于“其他”属性,如服务结果和/或属性,则采用MVVM模式...或者,您可以始终附加行为。 - Patrice Calvé

0

你在这部分是错误的:

var button = new Button();
button.Margin.Right = 10;

错误 CS1612:无法修改“System.Windows.FrameworkElement.Margin”的返回值,因为它不是变量。
这已经不是有效的代码了,因为Margin返回一个结构体,因此是值类型。而且,由于它没有从DependencyObject派生出来,许多DataBinding技巧也无法使用。
我只是想给出一个合适的解释,否则我会说你的第一个答案基本上是唯一的方法。

@dowhilefor 您对于代码后台部分是正确的。这是我的笔误。我已经更新了我的问题以反映我实际想表达的内容。然而,数据绑定将起作用,因为MarginRight属性是DependencyObject。因此,每当通过绑定更改属性值时,Margin将在回调方法中相应地进行调整。 - bugged87
@bugged87,我在谈论数据绑定时指的是原始问题,而不是你提供的附加属性答案。当然,数据绑定是有效的。但是对于一个结构体(如Thickness),除非它是源,否则无法使用数据绑定。简而言之,我不是在谈论MarginRight,而是在谈论Margin.Right。 - dowhilefor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接