0

我对 WPF 很陌生,(我昨天开始)而且我对数据绑定很困惑。我有一个窗口的视图模型,其中包含一个名为 Foo 的小部件,它有自己的视图模型。

小部件 Foo 将其 Visibility TwoWay(通过 BooleanToVisibilityConverter)绑定到其 FooViewModel 上的 bool 字段 Visible。FooViewModel 实现 INotifyPropertyChanged 并在设置 Visible 时触发 PropertyChanged 事件。

在 Window 的 Xaml 中,每当单击按钮时,它都会创建一个 Foo 。Window 的视图模型有另一个布尔字段,该字段将 TwoWay 绑定到其 Foo 视图实例的 Visibility。WIndow 的视图模型实现 INotifyPropertyChanged 并在修改布尔字段时触发 PropertyChanged 事件。

我希望这样做是每当窗口的布尔属性更改时,将设置 Foo 实例的可见性。发生这种情况时,我希望 Foo 的视图模型能够更新,因为 Foo 的可见性绑定是两种方式。当 Foo 视图模型更改其布尔字段时,我希望视图更改其可见性。此外,我希望 Window 视图模型被通知它的 Foo 实例不再可见,因此 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;
}

用户控件 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>

用户控件视图模型

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;
}
4

1 回答 1

0

绑定始终适用于属性。因此,当您在 Hide 方法中引发 Visible 时,您不会在实际属性中引发它。但是属性是绑定引擎将设置的。如果将它绑定到另一个依赖属性,它将不会收到有关它的通知。顺便提一句。什么是 TwoWayVisibilityConverter?BooleanToVisibilityConverter完全能够处理两种方式的绑定。

tl;博士要使两种方式绑定正常工作(实际上甚至是一种方式绑定),您需要正确实现 INotifyPropertyChanged这意味着,如果调用 setter 则提高属性。

public bool Visible
{
    get { return _visible; }
    set 
    { 
        _visible = value; 
        if (null != PropertyChanged)
            PropertyChanged(this, new PropertyChangedEventArgs("Visible")); 
    }
}
于 2013-05-15T15:49:57.140 回答