绑定不更新。 (属性 家长变更?!)
Binding does not update. (Property change of Parent?!)
我在更新绑定时遇到问题。
但我认为解释我的问题的最简单方法是我的代码:
XAML
<StackPanel>
<StackPanel.Resources>
<Converter:Converter_Position x:Key="Position"/>
</StackPanel.Resources>
<TextBox Text="{Binding Path=Position.X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Path=Position, Converter={StaticResource PositionToStartPosition}, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
如果我更改第一个文本框的文本,第二个文本框不会更新。
我的转换器:
class Converter_Position : IValueConverter
{
public object Convert(object value, Type t, object parameter, CultureInfo culture)
{
RaPoint Position = value as RaPoint;
return Position.ToString();
}
public object ConvertBack(object value, Type t, object parameter, CultureInfo culture)
{
throw new Exception();
}
}
邦德Class:
public class RaPoint : INodifyPropertyChanged
{
public RaPoint()
{
X = 0;
Y = 0;
}
public RaPoint(double X, double Y)
{
this.X = X;
this.Y = Y;
}
private const string XPropertyName = "X";
private double _X;
public double X
{
get
{
return _X;
}
set
{
_X = value;
RaisePropertyChanged(XPropertyName);
}
}
private const string YPropertyName = "Y";
private double _Y;
public double Y
{
get
{
return _Y;
}
set
{
_Y = value;
RaisePropertyChanged(YPropertyName);
}
}
public override string ToString()
{
return String.Format("X:{0} Y:{1}" , X.ToString(), Y.ToString());
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
数据上下文:
private const string PositionPropertyName = "Position";
private RaPoint _Position = new RaPoint();
public RaPoint Position
{
get
{
return _Position;
}
set
{
_Position = value;
RaisePropertyChanged(PositionPropertyName);
}
}
如果您将 Window 的 DataContext
设置为 RaPoint
实例 (Position
属性),那么您应该尝试以这种方式绑定:
<TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Converter={StaticResource PositionToStartPosition}, UpdateSourceTrigger=PropertyChanged}"/>
基本上,数据绑定在不同对象的两个属性之间建立连接。在第一行中,您绑定了在 DataContext 中设置的对象的 属性。 Path
用于指定该对象的 属性 或者可能指向 属性 的 属性 (假设 X 有一个 属性 Z,那么你可以做类似 Path=X.Z
).
的事情
关于第二个 TextBox
,如果您未指定绑定的 Source
、Path
、RelativeSource
或 ElementName
,则绑定将使用控件的 DataContext。 DataContext
通过视觉树从上层元素(例如 Window)传递到下层元素(在您的情况下为 TextBox
)。
但是这些建议并不能解决您的问题。当您更改第一个 TextBox 中 X
的值时,Position
属性 永远不会更改,因此不会调用 RaisePropertyChanged
,第二个 TextBox 也不会调用以 X 的新值更新。如果您想要一个具有 X
和 Y
值的文本框,则使用 MultiBinding。在您的 Window/UserControl 中执行此操作:
<TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource Position}">
<Binding Path="X" />
<Binding Path="Y" />
</MultiBinding>
</TextBox.Text>
</TextBox>
并以这种方式更改您的转换器:
public class Converter_Position : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return String.Format("X:{0} Y:{1}", values[0],values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
问题是转换器只在加载时使用一次。 Position
DataContext
永远不会改变,所以在位置 属性 的 setter 中有一个 RaisePropertyChnage
是没有意义的。删除那个。
public RaPoint Position
{
get
{
return _Position;
}
set
{
_Position = value;
RaisePropertyChanged(PositionPropertyName);
}
}
到
public RaPoint Position
{
get; set;
}
接下来是您希望 TextBox
在 X
更新时更新,因此理想情况下,您不想简单地绑定到 Position
(因为 DataContext
只被改变一次),你想绑定到 Position.X
因为那是 属性 changed 事件所在的地方。通过这样做,转换器将在每次 X
更改时进行评估。不过,您还只需要在转换器中传递的 Position
class 。您将需要更新转换器,以便将 Position
传递给它。最简单的方法是使用 MultiValuConverter 传递到参数对象以及 Paramter.X(将用于监视更改)。
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource PositionToStartPosition}">
<Binding Path="Position.X" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Position" />
</MultiBinding>
</TextBlock.Text>
</TextBox>
最后,更新您的转换器:
class Converter_Position : IMultiValueConverter
{
public object Convert(object[] values, Type t, object parameter, CultureInfo culture)
{
RaPoint Position = values[1] as RaPoint;
return Position.ToString();
}
public object ConvertBack(object[] values, Type t, object parameter, CultureInfo culture)
{
throw new Exception();
}
}
我在更新绑定时遇到问题。 但我认为解释我的问题的最简单方法是我的代码:
XAML
<StackPanel>
<StackPanel.Resources>
<Converter:Converter_Position x:Key="Position"/>
</StackPanel.Resources>
<TextBox Text="{Binding Path=Position.X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Path=Position, Converter={StaticResource PositionToStartPosition}, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
如果我更改第一个文本框的文本,第二个文本框不会更新。
我的转换器:
class Converter_Position : IValueConverter
{
public object Convert(object value, Type t, object parameter, CultureInfo culture)
{
RaPoint Position = value as RaPoint;
return Position.ToString();
}
public object ConvertBack(object value, Type t, object parameter, CultureInfo culture)
{
throw new Exception();
}
}
邦德Class:
public class RaPoint : INodifyPropertyChanged
{
public RaPoint()
{
X = 0;
Y = 0;
}
public RaPoint(double X, double Y)
{
this.X = X;
this.Y = Y;
}
private const string XPropertyName = "X";
private double _X;
public double X
{
get
{
return _X;
}
set
{
_X = value;
RaisePropertyChanged(XPropertyName);
}
}
private const string YPropertyName = "Y";
private double _Y;
public double Y
{
get
{
return _Y;
}
set
{
_Y = value;
RaisePropertyChanged(YPropertyName);
}
}
public override string ToString()
{
return String.Format("X:{0} Y:{1}" , X.ToString(), Y.ToString());
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
数据上下文:
private const string PositionPropertyName = "Position";
private RaPoint _Position = new RaPoint();
public RaPoint Position
{
get
{
return _Position;
}
set
{
_Position = value;
RaisePropertyChanged(PositionPropertyName);
}
}
如果您将 Window 的 DataContext
设置为 RaPoint
实例 (Position
属性),那么您应该尝试以这种方式绑定:
<TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Converter={StaticResource PositionToStartPosition}, UpdateSourceTrigger=PropertyChanged}"/>
基本上,数据绑定在不同对象的两个属性之间建立连接。在第一行中,您绑定了在 DataContext 中设置的对象的 属性。 Path
用于指定该对象的 属性 或者可能指向 属性 的 属性 (假设 X 有一个 属性 Z,那么你可以做类似 Path=X.Z
).
关于第二个 TextBox
,如果您未指定绑定的 Source
、Path
、RelativeSource
或 ElementName
,则绑定将使用控件的 DataContext。 DataContext
通过视觉树从上层元素(例如 Window)传递到下层元素(在您的情况下为 TextBox
)。
但是这些建议并不能解决您的问题。当您更改第一个 TextBox 中 X
的值时,Position
属性 永远不会更改,因此不会调用 RaisePropertyChanged
,第二个 TextBox 也不会调用以 X 的新值更新。如果您想要一个具有 X
和 Y
值的文本框,则使用 MultiBinding。在您的 Window/UserControl 中执行此操作:
<TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource Position}">
<Binding Path="X" />
<Binding Path="Y" />
</MultiBinding>
</TextBox.Text>
</TextBox>
并以这种方式更改您的转换器:
public class Converter_Position : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return String.Format("X:{0} Y:{1}", values[0],values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
问题是转换器只在加载时使用一次。 Position
DataContext
永远不会改变,所以在位置 属性 的 setter 中有一个 RaisePropertyChnage
是没有意义的。删除那个。
public RaPoint Position
{
get
{
return _Position;
}
set
{
_Position = value;
RaisePropertyChanged(PositionPropertyName);
}
}
到
public RaPoint Position
{
get; set;
}
接下来是您希望 TextBox
在 X
更新时更新,因此理想情况下,您不想简单地绑定到 Position
(因为 DataContext
只被改变一次),你想绑定到 Position.X
因为那是 属性 changed 事件所在的地方。通过这样做,转换器将在每次 X
更改时进行评估。不过,您还只需要在转换器中传递的 Position
class 。您将需要更新转换器,以便将 Position
传递给它。最简单的方法是使用 MultiValuConverter 传递到参数对象以及 Paramter.X(将用于监视更改)。
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource PositionToStartPosition}">
<Binding Path="Position.X" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="Position" />
</MultiBinding>
</TextBlock.Text>
</TextBox>
最后,更新您的转换器:
class Converter_Position : IMultiValueConverter
{
public object Convert(object[] values, Type t, object parameter, CultureInfo culture)
{
RaPoint Position = values[1] as RaPoint;
return Position.ToString();
}
public object ConvertBack(object[] values, Type t, object parameter, CultureInfo culture)
{
throw new Exception();
}
}