2013-05-15 4 views
0

Я очень новичок в WPF (я начал вчера), и я очень смущен привязкой данных. У меня есть модель просмотра для окна, которая содержит виджет Foo, который имеет свою собственную модель просмотра.Связывание данных TwoWay между различными элементами управления

Виджет Foo привязывает его видимость TwoWay (через BooleanToVisibilityConverter) к полю bool Видимый на его FooViewModel. FooViewModel реализует INotifyPropertyChanged и запускает событие PropertyChanged, когда установлен Visible.

В Xaml для окна он создает Foo всякий раз, когда нажимается кнопка. Модель представления Window имеет другое булевское поле, которое привязано TwoWay к видимости его экземпляра Foo View. Модель представления WIndow реализует INotifyPropertyChanged и запускает события PropertyChanged всякий раз, когда изменяется логическое поле.

Что я ожидаю от этого, всякий раз, когда изменяется логическое свойство окна, будет отображаться видимость экземпляра Foo. Когда это произойдет, я ожидаю, что View Model of Foo будет обновляться, поскольку привязка видимости Foo является двумя способами. Когда Foo View Model изменит свое логическое поле, я ожидаю, что View изменит его видимость. Кроме того, я ожидаю, что модель окна Window будет уведомлена о том, что ее экземпляр Foo больше не виден, и поэтому модель 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> 

Window View Model

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

Пожалуйста, посмотрите на [уценки помощь] (http://stackoverflow.com/editing-help) ... –

+0

Спасибо H.B. Я принял к сведению. – richardstartin

ответ

0

Привязка всегда работает на свойствах. Поэтому, пока вы поднимаете Visible в свой метод Hide, вы не поднимаете его в фактическом свойстве. Но свойство - это механизм привязки. Если вы привяжете это к другому свойству зависимости, он не получит уведомление об этом. КПП. Что такое TwoWayVisibilityConverter? BooleanToVisibilityConverter отлично подходит для двусторонней привязки.

Т.Л., д-р сделать двухстороннюю связывающую работу надлежащим образом (связывание и на самом деле даже один способ), необходимо реализовать INotifyPropertyChanged правильно, что означает, если сеттер называется поднять свойство.

public bool Visible 
{ 
    get { return _visible; } 
    set 
    { 
     _visible = value; 
     if (null != PropertyChanged) 
      PropertyChanged(this, new PropertyChangedEventArgs("Visible")); 
    } 
} 
+0

Да, я возвращаю его обратно в BooleanToVisibilityConverter, я был под недоразумением, что он будет работать только в одном направлении. Не могли бы вы предположить, что перемещение поднятия события PropertyChanged в тело сеттера является проблемой здесь? Есть ли в принципе что-то неправильно с ожиданием передачи информации от View Model to View Model так, как я пытался? – richardstartin

+0

Должен сказать, у меня были трудности с получением кода. Но это кажется мне самой очевидной проблемой. Есть редкие случаи, когда вы не хотите поднимать сеттера, но по другому методу. Но обычно повышение в сеттере - лучший способ. Если вы его не поднимаете, вызывается сеттер, но ваш взгляд не может его знать.В привязке данных нет магии, wpf прослушивает событие PropertyChanged и вызывает через отражение получателя поднятого свойства для обновления значения. если вы его не поднимаете, wpf этого не знает. – dowhilefor

+0

С уважением, я действительно не задаю вопрос, чтобы вы могли показать мне, как переместить заявление в средство настройки свойств - это больше, чем я не понимаю рамки. Я не понимаю, почему мне нужно запустить событие в сеттер. Если я установил значение в t0 и запустил событие в t1, я бы ожидал, что фреймворк получит мое событие, получит значение моего свойства и вернет значение, установленное в t0. Это предположение неверно? Если это неверно, то, конечно, мне не нужно запускать событие до тех пор, пока команда не выполнится, если я хочу отложить уведомление. Есть ли еще больше, чем это? Благодарю. – richardstartin

Смежные вопросы