与Notifyi的WPF绑定不会更新UI

时间:2014-09-10 20:19:24

标签: c# wpf xaml mvvm inotifypropertychanged

我的问题是,即使触发PropertyChanged,UI也不会更新。 XAML:

<ListBox Name="BookShelf" Visibility="Hidden" SelectedItem="{Binding SelectedItem}" Panel.ZIndex="1" Height="Auto" Grid.Column="3" Margin="8,50,0,0" HorizontalAlignment="Center"  ItemsSource="{Binding BookShelf}" Background="Transparent" Foreground="Transparent" BorderThickness="0" BorderBrush="#00000000">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel VerticalAlignment="Center" Orientation="Vertical">
                        <TextBlock FontSize="14" Margin="0,10,0,0" FontWeight="Bold" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Path=DbId}" />
                        <TextBlock FontSize="16" FontWeight="Bold" Width="170" TextWrapping="Wrap" Foreground="Black" Margin="5" HorizontalAlignment="Center" Text="{Binding Path=DisplayName}" />
                        <Image HorizontalAlignment="Center" Source="{Binding Path=bookImage}" Width="200" Height="200" Margin="0,0,0,10" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}"     SelectedItem="{Binding SelectedItem}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible" SelectionChanged="bookShelf_ComboBox_SelectionChanged"/>

视图模型:

public class BookShelfViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event ShowContentInBrowser ShowDatabaseContent;

    public BookShelfViewModel(ShowContentInBrowser showMethod)
    {
        ShowDatabaseContent += showMethod;
    }
    private ObservableCollection<DbInfo> _BookShelf = new ObservableCollection<DbInfo>();
    public ObservableCollection<DbInfo> BookShelf
    {
        get
        {
            if (_BookShelf == null)
                _BookShelf = new ObservableCollection<DbInfo>();
            return _BookShelf;
        }
        set
        {
            if (value != _BookShelf)
                _BookShelf = value;
        }
    }
    private DbInfo _selectedItem { get; set; }
    public DbInfo SelectedItem
    {
        get
        {
            return _selectedItem;
        }
        set
        {
            if (_selectedItem != value)
            {
                _selectedItem = value;
                RaisePropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
                if (_selectedItem == null)
                    return;
                if (_selectedItem.RelatedId != null)
                    ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);
                else
                    ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);

            }
        }
    }
    public void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
}

我使用的代码用于设置DataContext和SelectedItem:

await System.Windows.Application.Current.Dispatcher.BeginInvoke(
         DispatcherPriority.Background, new Action(
                () => this.BookShelf.SelectedItem = dbInfo
         )
      );

和DataContext:

await System.Windows.Application.Current.Dispatcher.BeginInvoke(
         DispatcherPriority.Background, new Action(
                () => this.BookShelf.DataContext = bookShelfViewModel
         )
       );

我对这个MVVM设计很新,据我所知,我读过的文章中,我找不到什么错误。我猜测使用Dispatcher不是必要的,但在这种情况下我认为不重要...... ListBox确实显示了我的对象,但更新SelectedItem是这里的问题......

更新

继承我的DbInfo代码:

public class DbInfo
{
    public int RelatedId { get; set; }
    public string DbId { get; set; }
    public TBase3.Article currentArticle { get; set; }
    public string LinkId { get; set; }
    public bool IsArticle { get; set; }
    public string folder { get; set; }
    public bool IsNamedArticle { get; set; }
    public int currentBlockIndex { get; set; }
    public int currentBlockCount { get; set; }
    public string DisplayName { get; set; }
    public int VScrollPos { get; set; }
    public int THTextVersion { get; set; }
    public bool isHtmlToc { get; set; }
    public ImageSource bookImage { get; set; }
}

提醒我什么时候为ViewModel设置新值 - &gt; SelectedItem和它转到PropertyChanged(this,e);线。它没有选择在ListBox中选择DbInfo。

UPDATE2:

我的窗口右侧有一个书籍清单,就像书架上的许多书。 它显示所有带滚动的书。被选中的书的内容正在窗口中显示。 但是,如果有理由我想从代码隐藏更改为另一本书,它将内容更新为webbrowser但不更新ListBox某些书作为SelectedItem

ANSWER : 好的,我现在找到了答案。设置BookShelf.SelectedItem = dbInfo的代码应该是bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x =&gt; x.DbId == dbInfo.DbIf);

3 个答案:

答案 0 :(得分:2)

await System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => this.BookShelf.DataContext = bookShelfViewModel));

看起来不太好,你在哪里这样做?我建议使用Galasoft MVVM Light和ViewModelLocator的用法来设置你的DataContext(通过nuget可用)。它为你提供了一个ViewModelBase,它具有你所有的属性需求和工作,你可以扩展到适合你的需要。听起来像DataContext,如果PropertyChanged甚至没有提出,可能是实际问题。

修改

As pointed out by Clemens the Mode=TwoWay in the binding is not needed here,因为它是SelectedItem属性的默认值,我只是留下它作为示例....

<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}"  SelectedItem="{Binding SelectedItem, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <command:EventToCommand Command="{Binding SelectedItemChangedCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

我注意到你的SelectionChanged =&#34; bookShelf_ComboBox_SelectionChanged&#34;在代码中,这是&#34; nasty&#34;,而是使用event to命令,因为你将viewmodel绑定到你的视图cbox。上面的代码将执行包含参数的命令,将以下内容添加到viewmodel。

public ICommand SelectedItemChangedCommand{ get; set;}

// somewhere in your code..
SelectedItemChangedCommand = new RelayCommand<SelectedItemChangedCommand>(OnSelectedItemChanged);

protected void OnSelectedItemChanged(SelectedItemChangedCommand e)
{
   // Do your magic here! Or why not just call this method from the setter of your bound property?
}

The WPF Cheat Sheet 是一个很好的紧凑列表,包含所有类型的绑定,非常方便,我曾经在墙上和家里都有它,当我学习WPF时:)< / p>

希望有所帮助

干杯

了Stian

答案 1 :(得分:0)

UI的意思是不更新?你设置了一个新的ItemsSource并且没有看到任何改变?

如果是这种情况,请将您的财产更改为

public ObservableCollection<DbInfo> BookShelf
{
    get
    {
        if (_BookShelf == null)
            _BookShelf = new ObservableCollection<DbInfo>();
        return _BookShelf;
    }
    set
    {
        if (value != _BookShelf)
        {   _BookShelf = value;
            RaisePropertyChanged(new PropertyChangedEventArgs("BookShelf"));
        }
    }

顺便说一句,我用另一种方式使用ObservableCollection。我只需初始化一次,然后使用Clear,Add,Remove。

如果ItemsSource不是您的问题,请发布DbInfo的代码,并为您的&#34;更新内容更新&#34;问题

答案 2 :(得分:0)

好的,我现在找到了答案。设置BookShelf.SelectedItem = dbInfo的代码应该是bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x =&gt; x.DbId == dbInfo.DbIf);