我对WPF很新,(我昨天开始),我对数据绑定非常困惑。我有一个Window模型,它包含一个名为Foo的小部件,它有自己的View Model。
小部件Foo将其Visibility TwoWay(通过BooleanToVisibilityConverter)绑定到其FooViewModel上的bool字段Visible。只要设置了Visible,FooViewModel就会实现INotifyPropertyChanged并触发PropertyChanged事件。
在Xaml for the Window中,只要单击一个按钮,它就会创建一个Foo。 Window的视图模型有另一个布尔字段,它将TwoWay绑定到其Foo View实例的Visibility。每当布尔字段被修改时,WIndow的视图模型实现INotifyPropertyChanged并触发PropertyChanged事件。
我期望这样做的是,只要窗口的布尔属性发生变化,就会设置Foo实例的可见性。当发生这种情况时,我希望更新Foo的View Model,因为Foo的可见性绑定是双向的。当Foo View Model更改其布尔字段时,我希望View更改其可见性。此外,我希望通知Window视图模型其Foo实例不再可见,因此Window的View模型将更新其自己的布尔字段。这是一个根本的误解吗?
我发布下面的(混淆)代码,如果它有助于揭示这种误解。感谢。
Window Xaml
<Window x:Class="XXX.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:XXX.ViewModel"
xmlns:v="clr-namespace:XXX"
Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterOwner" WindowState="Maximized">
<Window.Resources>
<vm:AppViewModel x:Key="AppViewModel"/>
<vm:TwoWayVisibilityConverter x:Key="BoolToVisibility" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource AppViewModel}}">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Connection" Command="{Binding Authenticate}"/>
<MenuItem Header="_About" Command="{Binding ShowAbout}"/>
<MenuItem Header="_Logout" Command="{Binding Logout}"/>
<MenuItem Header="_Configuration" Command="{Binding Configuration}"/>
<MenuItem Header="_Info" Command="{Binding ShowInfo}"/>
</Menu>
<StackPanel>
</StackPanel>
</DockPanel>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White"
Padding="10"
BorderThickness="0">
<TextBlock Text="XXX"/>
</Border>
<Grid x:Name="Overlay" Panel.ZIndex="1000" DataContext="{Binding Source={StaticResource AppViewModel}}">
<Border HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding Path=Modal, Converter={StaticResource BoolToVisibility}, Mode=OneWay}"
Background="DarkGray"
Opacity=".7" />
<v:Configuration HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding Path=ConfigurationVisible, Converter={StaticResource BoolToVisibility}, Mode=TwoWay}"/>
<v:Connect HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding Path=AuthenticateVisible, Converter={StaticResource BoolToVisibility}, Mode=TwoWay}"/>
</Grid>
</Grid>
窗口视图模型
class AppViewModel : INotifyPropertyChanged
{
[Import(typeof (IEventBus))] private IEventBus _bus;
public AppViewModel()
{
Authenticate = new ForwardCommand(obj => ShowAuthenticationView(), obj => !AuthenticateVisible);
Configuration = new ForwardCommand(obj => ShowConfigurationView(), obj => !ConfigurationVisible);
}
public bool Modal
{
get
{
return AuthenticateVisible || ConfigurationVisible;
}
}
public ICommand Authenticate { get; set; }
public bool AuthenticateVisible { get; set; }
public ICommand ShowInfo { get; set; }
public ICommand ShowAbout { get; set; }
public ICommand Logout { get; set; }
public ICommand Configuration { get; set; }
public bool ConfigurationVisible { get; set; }
private void ShowAuthenticationView()
{
AuthenticateVisible = !AuthenticateVisible;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("AuthenticateVisible"));
PropertyChanged(this, new PropertyChangedEventArgs("Modal"));
}
}
private void ShowConfigurationView()
{
ConfigurationVisible = !ConfigurationVisible;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("ConfigurationVisible"));
PropertyChanged(this, new PropertyChangedEventArgs("Modal"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
UserControl Xaml
<UserControl x:Class="XXX.Connect"
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:vm="clr-namespace:XXX.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<vm:ConnectViewModel x:Key="ViewModel"/>
<vm:TwoWayVisibilityConverter x:Key="BoolToVisibility" />
</UserControl.Resources>
<Grid Width="280"
Height="173"
DataContext="{Binding Source={StaticResource ViewModel}}"
Visibility="{Binding Path=Visible, Converter={StaticResource BoolToVisibility}, Mode=TwoWay}"
Background="White">
<Label Content="URL" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="102,12,0,0" Name="url" VerticalAlignment="Top" Width="169" Text="{Binding Path=Url, Mode=OneWayToSource}" TabIndex="0" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="102,70,0,0" Name="username" VerticalAlignment="Top" Width="171" Text="{Binding Path=Username, Mode=OneWayToSource}" TabIndex="2" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="102,41,0,0" Name="password" VerticalAlignment="Top" Width="169" Text="{Binding Path=Password, Mode=OneWayToSource}" TabIndex="1" />
<Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="12,39,0,0" Name="label3" VerticalAlignment="Top" />
<Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="12,68,0,0" Name="label2" VerticalAlignment="Top" />
<DockPanel HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="13">
<Button Content="OK" Height="23" HorizontalAlignment="Left" Margin="5" Name="ok" VerticalAlignment="Top" Width="75" Command="{Binding ConnectCommand}" TabIndex="3" />
<Button Content="Cancel" Height="23" HorizontalAlignment="Left" Margin="5" Name="cancel" VerticalAlignment="Top" Width="75" Command="{Binding CloseCommand}" TabIndex="4" />
</DockPanel>
</Grid>
</UserControl>
UserControl视图模型
internal class ConnectViewModel : INotifyPropertyChanged
{
[Import(typeof (IEventBus))] private IEventBus _bus;
public ConnectViewModel()
{
ConnectCommand = new ForwardCommand(obj => Fire(),
obj =>
Visible && !String.IsNullOrEmpty(Url) && !String.IsNullOrEmpty(Url) &&
!String.IsNullOrEmpty(Url));
CloseCommand = new ForwardCommand(obj => Hide(), obj => Visible);
}
public ICommand ConnectCommand { get; set; }
public ICommand CloseCommand { get; set; }
public string Url { get; set; }
public string Username { get; set; }
public string Password { get; set; }
private bool _visible;
public bool Visible
{
get { return _visible; }
set { _visible = value; }
}
private void Fire()
{
_bus.Publish<SessionCreatedEvent, SessionEventHandler>(new SessionCreatedEvent(Url, Username, Password));
Hide();
}
private void Hide()
{
Visible = false;
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
答案 0 :(得分:0)
绑定始终适用于属性。因此,当您在Hide方法中提升Visible时,不会在实际属性中提升它。但属性是绑定引擎将设置的属性。如果将此绑定到另一个依赖项属性,则不会收到有关它的通知。 顺便说一句。什么是TwoWayVisibilityConverter? BooleanToVisibilityConverter完全能够处理双向绑定。
tl; dr使双向绑定正常工作(实际上甚至是单向绑定)你需要实现INotifyPropertyChanged 正确,这意味着,如果调用setter则提升属性。
public bool Visible
{
get { return _visible; }
set
{
_visible = value;
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
}