Selector.IsSynchronizedWithCurrentItem

  • true if the SelectedItem is always synchronized with the current item in the ItemCollection;
  • false if the SelectedItem is never synchronized with the current item;
  • null if the SelectedItem is synchronized with the current item only if the Selector uses a CollectionView. The default value is null.

      //The following example binds two ListBox controls to the same ItemsSource. Because IsSynchronizedWithCurrentItem is set to true on each ListBox, the selected item is always the same for both controls
    
      <ListBox Name="employeeListBox1"
               ItemsSource="{Binding Source={StaticResource Employees}}"
               ItemTemplate="{StaticResource EmployeeItemTemplate}"
               IsSynchronizedWithCurrentItem="True"/>
    	
      <ListBox Name="employeeListBox2"
               ItemsSource="{Binding Source={StaticResource Employees}}"
               ItemTemplate="{StaticResource EmployeeItemTemplate}"
               IsSynchronizedWithCurrentItem="True"/>
    

Binding.RelativeSource & BindingExpression.ResolvedSource

可以通过BindingExpression.ResolvedSource获取ResolvedSource对象,但是貌似每次通过Binding获取RelativeSource对象时,都是null。

BindingExpression.UpdateSource & UpdateTarget

手动更新绑定。

绑定源

WPF绑定一个控件是使用Binding.ElementName,绑定非控件对象时使用Source,RelativeSource,DataContext属性(WPF特有,而非XAML),只能绑定对象的公有字段。

  1. ElementName
  2. Source
  3. RelativeSource
  4. DataContext,如果没有使用Source或RelativeSource属性指定源,WPF就从当前元素开始在元素树中向上查找。检查每个元素的DataContext属性,并使用第一个非空的DataContext属性。UI元素树的每一个结点都有DataContext,当Binding只知道自己的path ,而不知道source时,会沿着 UI元素树一路的向树根部找过去,路过节点,比较结点的DataContext是否具有Path所指定的属性,有的话,把对应的节点对象作为自己的source

绑定到静态变量属性

可以在XAML文件中添加一个命名空间,然后绑定到该命名空间的类的静态属性上。要注意,绑定的静态属性的类必须为public,例如,当绑定到资源字符串上时,需要手动把资源设置为public。

xmlns:Properties="clr-namespace:WpfApp3.Properties"

<Button x:Name="btn_Static" Content="{Binding Path=(Properties:Resources.String1)}" />
<Button x:Name="btn_Static2" Content="{x:Static Properties:Resources.String1}" />

日志文件夹

绑定在User Control的代码中定义的属性

ElementName=_this

<UserControl x:Class="SolutionExplorerUserControlLibrary.PMPPSolutionExplorerUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SolutionExplorerUserControlLibrary"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Name="_this">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <TextBlock Text="{Binding ElementName=_this, Path=DataDirectory}"></TextBlock>

TargetNullValue

如果设置了该属性,当数据源具有null值时,将显示提供的值。

<TextBox x:Name="txtSpecification" Text="{Binding Product.Specification, TargetNullValue=[No Descripton Provided]}"></TextBox>

绑定目标属性

绑定目标属性必须为依赖属性,目标属性的setter可以为private,但此时貌似不能直接通过XAML设置,只能通过C#代码设置,且模式只能为OneWayToSource。推荐把依赖属性全部设置为public。

public static readonly DependencyProperty TuningObjectGuidProperty = DependencyProperty.Register("TuningObjectGuid", typeof(string), typeof(RecipeLayoutUserControl), new FrameworkPropertyMetadata(Guid.Empty.ToString()));
public string TuningObjectGuid
{
    get => (string)GetValue(TuningObjectGuidProperty);
    private set => SetValue(TuningObjectGuidProperty, value); // private accessible setter
}

//Right code
Binding binding = new Binding();
binding.Mode = BindingMode.OneWayToSource;
binding.Path = new PropertyPath("PMOPUserControlModel.TuningObjectGuid");
BindingOperations.SetBinding(this.recipeLayout, RecipeLayoutUserControl.TuningObjectGuidProperty, binding);

//Wrong code
<lib:RecipeLayoutUserControl x:Name="recipeLayout"
                             TuningObjectGuid="{Binding Path=PMOPUserControlModel.TuningObjectGuid}" >

日志文件夹

验证

当验证失败时,每个控件都有一个附加属性System.Windows.Controls.Validation,记录验证错误。

if (Validation.GetHasError(productWindow.txtTotalProductQuantity))
{
    MessageBox.Show("error");
}

验证数据错误方法一: ExceptionValidationRule, 通过数据对象中引发异常实现

如果绑定中没有设置ExceptionValidationRule,那么绑定源引发的异常会被WPF直接忽略掉,文本框不会有任何变化或提示。当设置了ExceptionValidationRule,下述代码中Exception(“xyz”)会被WPF捕获,此时WPF的文本框会出现红色框线,代表数据出错。

<TextBox x:Name="txtMyProperty">
    <TextBox.Text>
        <Binding Path="MyProperty">
            <Binding.ValidationRules>
                <ExceptionValidationRule></ExceptionValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>


private int myVar;

public int MyProperty
{
    get { return myVar; }
    set {
        if (value > 100)
        {
            throw new Exception("xyz");
        }
        myVar = value;
    }
}

验证数据错误方法二: INotifyDataErrorInfo, 通过数据对象实现INotifyDataErrorInfo接口实现

WPF会自动检测绑定类有没有实现INotifyDataErrorInfo接口,如果实现了该接口,同样也会使用WPF控件通过视觉外观提示验证错误。此时需要确保绑定属性ValidatesOnNotifyDataErrors=True,True为默认值。

    private int myVar;

    public int MyProperty
    {
        get { return myVar; }
        set
        {
            if (value > 100)
            {
                List<string> errors = new List<string>();
                errors.Add("xyz");
                SetErrors(nameof(MyProperty), errors);
            }
            else
            {
                ClearErrors(nameof(MyProperty));
            }
            myVar = value;
        }
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();

    public bool HasErrors => errors.Count > 0;

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return errors.Values;
        }
        else
        {
            if (errors.ContainsKey(propertyName))
            {
                return errors[propertyName];
            }
            else
            {
                return null;
            }
        }
    }

    private void SetErrors(string propertyName, List<string> propertyErrors)
    {
        errors.Remove(propertyName);
        errors.Add(propertyName, propertyErrors);

        if (ErrorsChanged != null)
        {
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }
            
    }

    private void ClearErrors(string propertyName)
    {
        errors.Remove(propertyName);

        if (ErrorsChanged != null)
        {
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }

    }

验证数据方法三: 自定义验证规则ValidationRule

需要编写继承自ValidationRule的类,并添加到Binding.ValidationRules中,WPF会按顺序执行Binding.ValidationRules中的验证规格。

响应验证错误

通过设置Binding.NotifyOnValidationError为True,可以引发Error事件,该设置默认为False。Error事件为冒泡事件,所以可以在父控件中设置。

<Grid Validation.Error= "validationError">
    <StackPanel >
        <Label >test</Label>
        <TextBox x:Name="txtTest" Text="{Binding MyProperty, NotifyOnValidationError=True}" ></TextBox>
    </StackPanel>         
</Grid>

private void validationError(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
    {
        MessageBox.Show(e.Error.ErrorContent.ToString());
    }
}

窗口关闭前验证数据是否合法

    public void ExecuteOK(System.Windows.Window window)
    {
        StringBuilder sb = new StringBuilder();
        GetErrors(sb, window);
        string errorMessage = sb.ToString();
        if (!string.IsNullOrEmpty(errorMessage))
        {
            MessageBox.Show(errorMessage);
        }
        else
        {
            window.DialogResult = true;
            window.Close();
        }
    }

    private void GetErrors(StringBuilder sb, DependencyObject obj)
    {
        var s = LogicalTreeHelper.GetChildren(obj);
        foreach (object child in LogicalTreeHelper.GetChildren(obj))
        {
            if (child is DependencyObject)
            {
                TextBox element = child as TextBox;
                if (element != null && Validation.GetHasError(element))
                {
                    sb.Append(element.Text + " has errors:\r\n");
                    foreach (ValidationError error in Validation.GetErrors(element))
                    {
                        sb.Append("\t" + error.ErrorContent.ToString());
                        sb.Append("\r\n");
                    }
                    sb.Append("\r\n");
                }
                GetErrors(sb, (DependencyObject)child);
            }
        }
    }

路由事件绑定命令

    <lib:RecipeLayoutUserControl x:Name="recipeLayout" IsTuningEnabled="{Binding Path=PMOPUserControlModel.IsTuningEnabled}" RecipeGuid="{Binding Path=PMOPUserControlModel.RecipeGuid}" RefreshRecipe="{Binding Path=PMOPUserControlModel.RefreshRecipe}">
        <b:Interaction.Triggers>
            <b:EventTrigger EventName="TuningObjectTriggered" >
                <b:InvokeCommandAction Command="{Binding Path=TuningObjectTriggeredCommand}" CommandParameter="{Binding ElementName=recipeLayout, Path=TuningObjectGuid}"></b:InvokeCommandAction>
            </b:EventTrigger>
        </b:Interaction.Triggers>
    </lib:RecipeLayoutUserControl>

代码设定Binding

//可以在创建Binding对象时,直接初始化源对象的path属性
Binding binding1 = new Binding("MyProperty");
binding1.Source = this;

//如果创建Binding对象时,没有初始化源对象的path属性,那么需要手动设置Binding对的Path属性
//binding1.Path = new PropertyPath("MyProperty");

BindingOperations.SetBinding(this.Text1, TextBlock.TextProperty, binding1);
//this.Text1.SetBinding(TextBlock.TextProperty, binding1);

代码设置XML的Binding

When the binding source is XML data instead of a common language runtime (CLR) object, the XPath property is used instead of the Path property to indicate the path to the value on the binding source to use.

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(strXML);

XmlDataProvider xmlDataProvider = new XmlDataProvider();
xmlDataProvider.Document= xmlDocument;
xmlDataProvider.XPath = "Inventory/Books"; //使用XPath生成集合

Binding binding=new Binding("");
binding.Source = xmlDataProvider;
binding.XPath = "*[@Stock='out'] | *[@Number>=8 or @Number=3]";
BindingOperations.SetBinding(this.listTest, ListBox.ItemsSourceProperty, binding);