从绑定设置边距

16

我有一个绑定值,它返回一个int,代表我想要分配给元素左右边距的值。

这是我尝试过的,但它无法编译通过。

如果我设置整个边距,它可以工作,但我只想要左右两侧的边距。

Xaml:

<Image x:Name="_image" Source="mat.png" Margin="{Binding EditorRow.BondIndent},0,{Binding EditorRow.BondIndent},0" />

类:

public int BondIndent
{
    get { return _bondSequence * 5; }
}

3
要返回一个 Thickness 类型的值怎么样? - default
默认情况下,我可以控制从类后面返回的内容。请您能否添加一个基于返回厚度的答案,我将标记为答案。 - user589195
添加了一个答案。希望对你有帮助。 - default
4个回答

30

返回边距?

public Thickness Margin
{
    get { return new Thickness(BondIndent,0,BondIndent,0);}
}

那么请更改为:

<Image x:Name="_image" Source="mat.png" Margin="{Binding EditorRow.Margin}" />

15

你可能需要使用ValueConverter。例如:

public class LeftRightThicknessConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is int)
        {
            int margin = (int)value;
            return Thickness(margin, 0, margin, 0);
        }
        return Thickness();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

您可以按照以下方式使用转换器:

<Grid>
    <Grid.Resources>
        <xxx:LeftRightThicknessConverter x:Key="LeftRightThicknessConverter" />
    </Grid.Resources>

    <Image Margin="{Binding SomePropertyPath, Converter={StaticResource LeftRightThicknessConverter}}" />
</Grid>
假设 xxx 是一个有效的XML命名空间。

1
OP如何使用这个值转换器? - default
值转换器可以是静态的并存储在另一个程序集中吗? - user589195
您可以在另一个程序集中定义转换器类的定义。但是,您需要从XAML中访问实例。通常,您会像上面的示例一样在元素的资源中(例如UserControl元素)创建一个实例。您可以通过将xmlns:xxx="clr-namespace:MyOtherAssembly.TheNamespace;assembly=MyOtherAssembly"添加到用户控件或模板控件的根节点来定义引用您程序集(和特定命名空间)的XML命名空间。当然,要使用适当的名称替换“xxx”。 - odyss-jii
@odyss-jii 既然 OP 没有使用 MVVM,我认为没有必要进行如此程度的解耦。他的属性的性质假定视图的 DataContext 正在决定事物应该如何呈现... 这意味着更好(更简单)的解决方案只是返回一个 Thickness。 - Daniel
@Doc 可能是这样,但我希望保留原始问题提出者的数据模型。 - odyss-jii
显示剩余2条评论

2

你可以返回一个Thickness而不是一个int,因为这才是Margin所代表的:

public Thickness BondIndent
{
    get
    {
        int margin = _bondSequence * 5;
        return new Thickness(margin, 0, margin, 0);
    }
}

你的示例之所以有效,是因为Thickness有重载构造函数,可以接受1、2或4个参数。当调用只接受一个参数的构造函数时,所有边都将初始化为该值。WPF会自动将其转换为基于绑定值的Thickness
另外,BondIndent现在可能更好地称为BondMarginBondThickness

1

我刚刚编写了一些附加属性,可以通过绑定或静态资源轻松设置单个Margin值:

WPF:

public class Margin
{
    public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
        "Left",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0, LeftChanged));

    private static void LeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var frameworkElement = d as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;
            frameworkElement.Margin = new Thickness((double)e.NewValue, currentMargin.Top, currentMargin.Right, currentMargin.Bottom);
        }
    }

    public static void SetLeft(UIElement element, double value)
    {
        element.SetValue(LeftProperty, value);
    }

    public static double GetLeft(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
        "Top",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0, TopChanged));

    private static void TopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var frameworkElement = d as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;
            frameworkElement.Margin = new Thickness(currentMargin.Left, (double)e.NewValue, currentMargin.Right, currentMargin.Bottom);
        }
    }

    public static void SetTop(UIElement element, double value)
    {
        element.SetValue(TopProperty, value);
    }

    public static double GetTop(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty RightProperty = DependencyProperty.RegisterAttached(
        "Right",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0, RightChanged));

    private static void RightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var frameworkElement = d as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;
            frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, (double)e.NewValue, currentMargin.Bottom);
        }
    }

    public static void SetRight(UIElement element, double value)
    {
        element.SetValue(RightProperty, value);
    }

    public static double GetRight(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty BottomProperty = DependencyProperty.RegisterAttached(
        "Bottom",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0, BottomChanged));

    private static void BottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var frameworkElement = d as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;
            frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, currentMargin.Right, (double)e.NewValue);
        }
    }

    public static void SetBottom(UIElement element, double value)
    {
        element.SetValue(BottomProperty, value);
    }

    public static double GetBottom(UIElement element)
    {
        return 0;
    }
}

UWP:
public class Margin
{
    public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
        "Left",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0));

    public static void SetLeft(UIElement element, double value)
    {
        var frameworkElement = element as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;

            frameworkElement.Margin = new Thickness(value, currentMargin.Top, currentMargin.Right, currentMargin.Bottom);
        }
    }

    public static double GetLeft(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
        "Top",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0));

    public static void SetTop(UIElement element, double value)
    {
        var frameworkElement = element as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;

            frameworkElement.Margin = new Thickness(currentMargin.Left, value, currentMargin.Right, currentMargin.Bottom);
        }
    }

    public static double GetTop(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty RightProperty = DependencyProperty.RegisterAttached(
        "Right",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0));

    public static void SetRight(UIElement element, double value)
    {
        var frameworkElement = element as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;

            frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, value, currentMargin.Bottom);
        }
    }

    public static double GetRight(UIElement element)
    {
        return 0;
    }

    public static readonly DependencyProperty BottomProperty = DependencyProperty.RegisterAttached(
        "Bottom",
        typeof(double),
        typeof(Margin),
        new PropertyMetadata(0.0));

    public static void SetBottom(UIElement element, double value)
    {
        var frameworkElement = element as FrameworkElement;
        if (frameworkElement != null)
        {
            Thickness currentMargin = frameworkElement.Margin;

            frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, currentMargin.Right, value);
        }
    }

    public static double GetBottom(UIElement element)
    {
        return 0;
    }
}

使用方法:

<TextBlock Text="Test"
    app:Margin.Top="{Binding MyValue}"
    app:Margin.Right="{StaticResource MyResource}"
    app:Margin.Bottom="20" />

很好的一点是它们不会覆盖Margin上的其他值,因此您也可以将它们组合在一起。

设置器和获取器不是直接调用的。你必须向DependencyProperty添加一个PropertyCallback。我真的不敢相信,你已经测试过了 :) - Nicolas
@Nicolas https://msdn.microsoft.com/zh-cn/library/ms749011(v=vs.110).aspx 向下滚动至“自定义附加属性”->“如何创建附加属性”。它们使用命名约定来获取和设置方法,您不需要在DependencyProperty上提供属性回调。我们已经在我们的代码中广泛使用它;它确实有效。 - RandomEngy
Blend和Visual Studio Designer使用这些函数,但WPF不使用。这些方法只是通过代码后台获取/设置属性的有趣方式...但在您的示例中,您可以在XAML中设置这些属性(而不是代码后台)。因为WPF正在管理信息,所以除了设置和获取信息之外,您不应该将逻辑放入这些方法中。 - Nicolas
@ Nicolas 哦,WPF 的工作方式略有不同。添加了一个在那里适用的附加属性版本。 - RandomEngy

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