最基本的绑定(必须在一个可视化树中间,例如后台代码生成就不行)
<StackPanel>
<TextBlock Text="{Binding ElementName=txt,Path=Text,StringFormat='Input:{0}'}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel},Path=Children[2].Text,StringFormat='Input:{0}'}"/>
<TextBox x:Name="txt"/>
</StackPanel>
如果是后台代码生成的话,需要添加控件到NameScope中
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var box = new TextBox
{
Name = "txt"
};
this.RegisterName(box.Name, box);
panel.Children.Add(box);
}
<StackPanel x:Name="panel">
<TextBlock Text="{Binding ElementName=txt,Path=Text,StringFormat='Input:{0}'}"/>
// 这行不起作用的原因是越界了
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel},Path=Children[2].Text,StringFormat='Input:{0}'}"/>
<!--<TextBox x:Name="txt"/>-->
</StackPanel>
绑定失败原因:因为在可视化树中找不到Button.Tag、DataGrid.Columns(直接不显示)
<StackPanel x:Name="panel">
<TextBlock Name="tbl" Text="Hello world!"/>
<Button HorizontalAlignment="Center">
<Button.Tag>
<ItemsControl>
<TextBlock Text="{Binding ElementName=tbl,Path=Text}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel},Path=Children[0].Text}"/>
</ItemsControl>
</Button.Tag>
</Button>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="{Binding ElementName=tbl,Path=Text}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
解决方法:将绑定换为Source={x:Reference Name=tbl},Path=Text
原因:使用x:Reference
<Button.Tag>
<ItemsControl>
<TextBlock Text="{Binding ElementName=tbl,Path=Text}"/>
<TextBlock Text="{Binding Source={x:Reference Name=tbl},Path=Text}"/>
</ItemsControl>
</Button.Tag>
使用x:Reference有个弊端:存在循环依赖无法被解析
以下面例子举例,在button还没有被解析完,就不能绑定到它身上
<Button x:Name="tbn" HorizontalAlignment="Center" Content="Click Me">
<Button.Tag>
<ItemsControl>
<TextBlock Text="{Binding Source={x:Reference Name=tbn},Path=Content}"/>
</ItemsControl>
</Button.Tag>
</Button>
特殊情况,内容(MenuItem)跑到可视化树的其他地方(这个控件的作用是右键点出东西)
同样情况还有ToolTip属性(这个属性的作用是鼠标悬空后,弹出相应信息)(如果直接写在button的属性里,也可以直接显示出来)
上述属性在可视化树里面都是在PopupRoot里
<Button x:Name="tbn" HorizontalAlignment="Center" Content="Click Me">
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem" Header="{Binding ElementName=tbl,Path=Text}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
解决方法1:使用x:Reference
解决方法2:将datacontext=viewmodel,直接绑定对应属性(xxx= “{Binding Message}”)
解决方法3:通过RelativeSource来访问
<Button x:Name="tbn" HorizontalAlignment="Center" Content="Click Me">
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem" Header="{Binding Source={x:Reference Name=tbl},Path=Text}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button x:Name="tbn" HorizontalAlignment="Center" Content="Click Me">
<Button.ToolTip>
<ToolTip>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip},Path=PlacementTarget.Content}"/>
</ToolTip>
</Button.ToolTip>
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem" Header="{Binding Source={x:Reference Name=tbl},Path=Text}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
传递参数的时候尽量使用RelativeSource而不是ElementName
// 这个代码起作用
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem" Header="Foo" Command="{Binding FooCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
</ContextMenu>
</Button.ContextMenu>
// 这段代码不起作用
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem" Header="Foo" Command="{Binding FooCommand}" CommandParameter="{Binding ElementName=context}"/>
</ContextMenu>
</Button.ContextMenu>
万能方法1(BIndingProxy要更加轻量)
万能方法2()
首先创建一个类BindingProxy
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore() => new BindingProxy();
public object Data
{
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
nameof(Data),
typeof(object),
typeof(BindingProxy)
);
}
然后再在xmal中引用
<Window.Resources>
// 首先要加入资源里面
<local:BindingProxy x:Key="MyButton" Data="{Binding ElementName=btn}"/>
</Window.Resources>
<StackPanel x:Name="panel">
<TextBlock Name="tbl" Text="Hello world!"/>
<Button x:Name="btn" HorizontalAlignment="Center" Content="Click Me!!!">
<Button.ToolTip>
<ToolTip>
// 在这里引用
<TextBlock Text="{Binding Source={StaticResource MyButton},Path=Data.Content}"/>
</ToolTip>
</Button.ToolTip>
</StackPanel>
方法二
<Window.Resources>
<local:BindingProxy x:Key="MyButton" Data="{Binding ElementName=btn}"/>
// 这里添加引用
<DiscreteObjectKeyFrame x:Key="KeyFrame" Value="{Binding ElementName=window}"/>
</Window.Resources>
<StackPanel x:Name="panel">
<TextBlock Name="tbl" Text="Hello world!"/>
<Button x:Name="btn" HorizontalAlignment="Center" Content="Click Me!!!">
<Button.ToolTip>
<ToolTip>
<TextBlock Text="{Binding Source={StaticResource MyButton},Path=Data.Content}"/>
</ToolTip>
</Button.ToolTip>
<Button.ContextMenu>
<ContextMenu Name="context">
// 在这里使用
<MenuItem Name="menuitem" Header="Foo" Command="{Binding Source={StaticResource KeyFrame},Path=Value.DataContext.FooCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
笨方法:在后台进行绑定
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var binding = new Binding()
{
Source = tbl,
Path = new PropertyPath(TextBlock.TextProperty)
};
// 绑定方法1
BindingOperations.SetBinding(menuitem,MenuItem.HeaderProperty, binding);
// 绑定方法2
menuitem.SetBinding(MenuItem.HeaderProperty, binding);
}
}
<StackPanel x:Name="panel">
<TextBlock Name="tbl" Text="Hello world!"/>
<Button x:Name="btn" HorizontalAlignment="Center" Content="Click Me!!!">
<Button.ToolTip>
<ToolTip>
<TextBlock Text="{Binding Source={StaticResource MyButton},Path=Data.Content}"/>
</ToolTip>
</Button.ToolTip>
<Button.ContextMenu>
<ContextMenu Name="context">
<MenuItem Name="menuitem"
Command="{Binding Source={StaticResource KeyFrame},Path=Value.DataContext.FooCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>