WPF SetBinding

WPF 通过后台绑定数据

简介

WPF 中的 Data binding 为应用程序呈现数据和与数据交互提供了一种简单且一致的方式。元素可以以 .NET 对象和 XML 的形式绑定到来自不同类型数据源的数据。
但是有些特殊情况下只能通过后台 FrameworkElement.SetBinding 绑定数据。

示例

示例为项目需要迁移到 .Net Framework 4.5.2 的 FFME 库。

SetBinding 介绍

第一个参数为用户控件的 依赖属性
第二个参数为 Binding 类,同时可以设置要使用的转换器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace System.Windows
{
//
// 摘要:
// 为 Windows Presentation Foundation (WPF) 元素提供一套 WPF 框架级别的属性、事件和方法。 此类表示所提供的 WPF
// 框架级别实现基于 System.Windows.UIElement 定义的 WPF 核心级别 APIs。
[RuntimeNameProperty("Name")]
[StyleTypedProperty(Property = "FocusVisualStyle", StyleTargetType = typeof(Control))]
[UsableDuringInitialization(true)]
[XmlLangProperty("Language")]
public class FrameworkElement : UIElement, IFrameworkInputElement, IInputElement, ISupportInitialize, IHaveResources, IQueryAmbient
{
//
// 摘要:
// 基于已提供的绑定对象将一个绑定附加到此元素上。
//
// 参数:
// dp:
// 标识应在其中建立绑定的属性。
//
// binding:
// 表示数据绑定的详细信息。
//
// 返回结果:
// 记录绑定的条件。 此返回值可用于错误检查。
public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding);
}
}

公共 IValueConverter 方法

用于提供将自定义逻辑应用于绑定的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
internal class TimeSpanToSecondsConverter : IValueConverter
{
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch (value)
{
case TimeSpan span:
return span.TotalSeconds;
case Duration duration:
return duration.HasTimeSpan ? duration.TimeSpan.TotalSeconds : 0d;
default:
return 0d;
}
}

/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double == false) return 0d;
var result = TimeSpan.FromTicks(System.Convert.ToInt64(TimeSpan.TicksPerSecond * (double)value));

// Do the conversion from visibility to bool
if (targetType == typeof(TimeSpan)) return result;
return targetType == typeof(Duration) ?
new Duration(result) : Activator.CreateInstance(targetType);
}
}

前台绑定 Binding

App.xaml

1
2
3
<ResourceDictionary>
<local:TimeSpanToSecondsConverter x:Key="TimeSpanToSecondsConverter" />
</ResourceDictionary>

UserControl.xaml

1
2
3
4
5
6
7
8
<Slider Name="PositionSlider" Grid.Row="0" Margin="10,0" Cursor="Hand"
IsSnapToTickEnabled="False"
IsEnabled="{Binding MediaElement.IsOpen}"
SmallChange="{Binding MediaElement.PositionStep, Converter={StaticResource TimeSpanToSecondsConverter}}"
LargeChange="{Binding MediaElement.PositionStep, Converter={StaticResource TimeSpanToSecondsConverter}}"
Minimum="{Binding MediaElement.PlaybackStartTime, Converter={StaticResource TimeSpanToSecondsConverter}}"
Maximum="{Binding MediaElement.PlaybackEndTime, Converter={StaticResource TimeSpanToSecondsConverter}}"
Value="{Binding MediaElement.Position, Converter={StaticResource TimeSpanToSecondsConverter}}" />

UserControlViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private MediaElement m_MediaElement;
/// <summary>
/// Gets the media element hosted by the main window.
/// </summary>
public MediaElement MediaElement
{
get
{
if (m_MediaElement == null)
m_MediaElement = (Application.Current.MainWindow as MainWindow)?.Media;

return m_MediaElement;
}
}

后台绑定 SetBinding

UserControl.xaml

1
<Slider Name="PositionSlider" Grid.Row="0" Margin="10,0" Cursor="Hand" IsSnapToTickEnabled="False"/>

UserControl.xaml.cs

1
2
3
4
5
6
7
8
PositionSlider.SetBinding(Slider.IsEnabledProperty, new Binding("IsOpen") { Source = this._innerPlayer });

IValueConverter valueConverter = new TimeSpanToSecondsConverter();
PositionSlider.SetBinding(Slider.SmallChangeProperty, new Binding("PositionStep") { Source = this._innerPlayer, Converter = valueConverter });
PositionSlider.SetBinding(Slider.LargeChangeProperty, new Binding("PositionStep") { Source = this._innerPlayer, Converter = valueConverter });
PositionSlider.SetBinding(Slider.MinimumProperty, new Binding("PlaybackStartTime") { Source = this._innerPlayer, Converter = valueConverter });
PositionSlider.SetBinding(Slider.MaximumProperty, new Binding("PlaybackEndTime") { Source = this._innerPlayer, Converter = valueConverter });
PositionSlider.SetBinding(Slider.ValueProperty, new Binding("Position") { Source = this._innerPlayer, Converter = valueConverter });