#.net #silverlight #binding
#.net #silverlight #привязка
Вопрос:
Допустим, у меня есть граница, DataContext которой является объектом типа MyViewModel. MyViewModel имеет свойства bool, называемые RoundLeft и RoundRight. Когда значение RoundLeft равно true, я хочу, чтобы угловой радиус границы был равен 6,0,0,6. Когда RoundRight имеет значение true, я хочу 0,6,6,0. Когда оба значения истинны, я хочу 6,6,6,6.
Ниже я описал свои первые две попытки. Я еще не сдался, но я хотел посмотреть, могут ли у кого-нибудь еще быть какие-либо идеи.
Попытка # 1
Я заставил его частично работать, привязав к самому экземпляру MyViewModel (не к определенному свойству) и используя IValueConverter, который создает правильный объект cornerRadius. Это работает при начальной загрузке. Проблема в том, что привязка отслеживает изменения объекта в целом, а не изменения конкретных свойств RoundLeft и RoundRight, например, если RoundLeft изменяется, cornerRadius границы этого не делает.
Привязка:
<Border CornerRadius="{Binding Converter={StaticResource myShiftCornerRadiusConverter}}" />
Конвертер:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var myViewModel = value as MyViewModel;
if (myViewModel != null)
{
return new CornerRadius(
myViewModel.RoundLeft ? 6 : 0,
myViewModel.RoundRight ? 6 : 0,
myViewModel.RoundRight ? 6 : 0,
myViewModel.RoundLeft ? 6 : 0);
}
else
{
return new CornerRadius(6);
}
}
Попытка # 2
Это сообщение в блоге Колина Эберхардта выглядело многообещающе, но я получаю расплывчатые XamlParseExceptions и COMExceptions. Вот мой XAML:
<Border>
<ce:MultiBindings>
<ce:MultiBinding TargetProperty="CornerRadius" Converter="{StaticResource myCornerRadiusConverter}">
<ce:MultiBinding.Bindings>
<ce:BindingCollection>
<Binding Path="RoundLeft" />
<Binding Path="RoundRight" />
</ce:BindingCollection>
</ce:MultiBinding.Bindings>
</ce:MultiBinding>
</ce:MultiBindings>
</Border>
Вот мой конвертер, хотя выполнение никогда не заходит так далеко, т. Е. моя точка останова никогда не достигается.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Length == 2 amp;amp; values.All(v => v is bool))
{
var roundLeft = (bool)values[0];
var roundRight = (bool)values[1];
return new CornerRadius(
roundLeft ? 6 : 0,
roundRight ? 6 : 0,
roundRight ? 6 : 0,
roundLeft ? 6 : 0);
}
else
{
return new CornerRadius(6);
}
}
Ответ №1:
Отсутствие встроенной поддержки множественной привязки в SL делает это немного болезненным, но как насчет более простого (хотя и немного более связанного) подхода?
Поскольку у вас уже есть свойства roundLeft и roundRight в вашей виртуальной машине, которые уже в некоторой степени связаны с определенной парадигмой пользовательского интерфейса. Так почему бы просто не иметь вычисляемое свойство, которое возвращает значение cornerRadius, и просто привязать к нему?
Так, например, при изменении roundLeft вы вызываете метод для обновления вычисляемого свойства cornerRadius и вызываете уведомление об изменении этого свойства, а затем ваше представление привязывается к вычисляемому свойству cornerRadius.
Комментарии:
1. 1 Спасибо за ответ; Я реализовал ваше предложение, и оно сработало. Однако затем я придумал другое решение, которое сохраняет все содержимое cornerRadius на уровне просмотра, поэтому я выбрал это.
Ответ №2:
Как насчет DataTrigger и соответствующего свойства модели:
<Border Height="25" Width="45" BorderThickness="5" BorderBrush="Green">
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Round}" Value="Left">
<Setter Property="Border.CornerRadius" Value="6,0,0,6"/>
</DataTrigger>
<DataTrigger Binding="{Binding Round}" Value="Right">
<Setter Property="Border.CornerRadius" Value="0,6,6,0"/>
</DataTrigger>
<DataTrigger Binding="{Binding Round}" Value="Both">
<Setter Property="Border.CornerRadius" Value="6"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Text}"/>
</Border>
Комментарии:
1. AFAIK StyleTriggers не поддерживаются в Silverlight, и, более конкретно, я не верю, что DataTriggers также существуют.
2. @Foovandadil — Я этого не знал. Вау. Это отстой.
3. Существуют триггеры данных, но не триггеры стилей.
4. @Rich Melton да, да, это действительно отстой.
5. Спасибо за ваше предложение; Я хотел бы им воспользоваться!
Ответ №3:
Я реализовал подход, предложенный @Foovanadil, но затем мне пришла в голову другая идея: я создал новый ContentControl, который предоставляет свойства зависимости RoundLeft и RoundRight. Это, конечно, потребовало больше кода, но теперь все содержимое cornerRadius находится на уровне просмотра.
[TemplatePart(Name = _borderPartName, Type = typeof(Border))]
public class CustomRoundedBorder : ContentControl
{
#region Private Fields
private const string _borderPartName = "PART_Border";
private Border _borderPart;
#endregion
#region Dependency Properties
#region DefaultCornerRadius
//////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets or sets the default corner radius, in pixels.
/// </summary>
//////////////////////////////////////////////////////////////////////////////
public double DefaultCornerRadius
{
get { return (double)GetValue(DefaultCornerRadiusProperty); }
set { SetValue(DefaultCornerRadiusProperty, value); }
}
public static readonly DependencyProperty DefaultCornerRadiusProperty = DependencyProperty.Register(
"DefaultCornerRadius", typeof(double), typeof(CustomRoundedBorder),
new PropertyMetadata(new PropertyChangedCallback(RoundingChanged)));
#endregion
#region RoundLeft
//////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets or sets a value indicating whether to round the corners on the left side of the border.
/// </summary>
//////////////////////////////////////////////////////////////////////////////
public bool RoundLeft
{
get { return (bool)GetValue(RoundLeftProperty); }
set { SetValue(RoundLeftProperty, value); }
}
public static readonly DependencyProperty RoundLeftProperty = DependencyProperty.Register(
"RoundLeft", typeof(bool), typeof(CustomRoundedBorder),
new PropertyMetadata(new PropertyChangedCallback(RoundingChanged)));
#endregion
#region RoundRight
//////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets or sets a value indicating whether to round the corners on the left side of the border.
/// </summary>
//////////////////////////////////////////////////////////////////////////////
public bool RoundRight
{
get { return (bool)GetValue(RoundRightProperty); }
set { SetValue(RoundRightProperty, value); }
}
public static readonly DependencyProperty RoundRightProperty = DependencyProperty.Register(
"RoundRight", typeof(bool), typeof(CustomRoundedBorder),
new PropertyMetadata(new PropertyChangedCallback(RoundingChanged)));
#endregion
#region EffectiveCornerRadius
//////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the effective corner radius, based on DefaultCornerRadius and
/// RoundLeft and RoundRight.
/// </summary>
//////////////////////////////////////////////////////////////////////////////
public double EffectiveCornerRadius
{
get { return (double)GetValue(EffectiveCornerRadiusProperty); }
private set { SetValue(EffectiveCornerRadiusProperty, value); }
}
public static readonly DependencyProperty EffectiveCornerRadiusProperty = DependencyProperty.Register(
"EffectiveCornerRadius", typeof(double), typeof(CustomRoundedBorder),
new PropertyMetadata(new PropertyChangedCallback(RoundingChanged)));
#endregion
#endregion
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this._borderPart = this.GetTemplateChild(_borderPartName) as Border;
this.UpdateCornerRadius();
}
#endregion
#region Private Methods
private static void RoundingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CustomRoundedBorder;
if (control != null)
{
control.UpdateCornerRadius();
}
}
private void UpdateCornerRadius()
{
if (this._borderPart != null)
{
this._borderPart.CornerRadius = new CornerRadius(
this.RoundLeft ? this.DefaultCornerRadius : 0,
this.RoundRight ? this.DefaultCornerRadius : 0,
this.RoundRight ? this.DefaultCornerRadius : 0,
this.RoundLeft ? this.DefaultCornerRadius : 0);
}
}
#endregion
}
Затем я создал для него ControlTemplate (некоторые свойства опущены для краткости):
<ControlTemplate x:Key="MyBorderTemplate" TargetType="ce:CustomRoundedBorder">
<Border
x:Name="PART_Border"
CornerRadius="{TemplateBinding EffectiveCornerRadius}"
>
<ContentPresenter />
</Border>
</ControlTemplate>
Затем вот где я привязал его к свойствам view-model:
<ce:CustomRoundedBorder
DefaultCornerRadius="6"
RoundLeft="{Binding RoundLeft}"
RoundRight="{Binding RoundRight}"
Template="{StaticResource MyBorderTemplate}"
>
<!-- Content -->
</ce:CustomRoundedBorder>