1

我构建了一个 Office 2010 Word 插件,向其中添加了一个 Windows 窗体元素,并在包含我的 WPF 的 ElementHost 对象中添加了一个。

这就是问题所在:我遇到了问题,在 Xaml 代码中定义并在 ViewModel 实现中设置的公共属性(DP)没有更新(参见下面的代码),即使我在调试时找不到任何错误代码。这仅适用于我得到的属性,此外,我得到了一些 ObservableCollection<> 列表,它们的更新没有任何问题。有人有想法吗?我认为我可以在代码中自己定义属性,如下所示:

public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource",typeof(string),typeof(usercontrol1));

但这也不起作用(例如,我刚刚将此行添加到 Xaml 文件的代码隐藏文件中 - 并留下了其他任何内容)

我的代码看起来是这样的:

Forms 元素的构造函数(它实际上是私有的,因为我将它实现为 Singleton):

private Sidebar()
{
    InitializeComponent();
    this.DoubleBuffered = true;

    _wpfHost = new ElementHost();
    _wpfHost.Dock = DockStyle.Fill;

    _wpfUserControl = new WPFUI();

    _wpfHost.Child = _wpfUserControl;

    this.Controls.Add(_wpfHost);
}

此外,我创建了一个底层 ViewModel 并将其设置为 WPFUI 的 DataContext 属性,就像这样(它也是一个单例实现,因为我想在进一步的实现中从多个地方访问同一个实例,但此时这不会进入游戏):

public WPFUI()
{
    InitializeComponent();

    this.DataContext = myViewModel.GetInstance();
}

我的 ViewModel 属性是这样定义和使用的:

public ObservableCollection<myListViewItem> PeopleEntityResultObjects { get; private set; }

private string _NumberOfPeople;
public string NumberOfPeople
{
    get { return _NumberOfPeople; }
    set { SetField(ref _NumberOfPeople, value, () => NumberOfPeople); }
}

最后 Xaml 看起来像这样:

<TabControl>
    <TabItem>
        <TabItem.Header>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text=" People "/>
                <TextBlock Text="{Binding Path=NumberOfPeople}"/>
            </StackPanel>
        </TabItem.Header>

        <ScrollViewer x:Name="PeopleListScrollViewer">
            <ListView Name="PeopleListView" ItemsSource="{Binding Path=PeopleEntityResultObjects, Mode=OneWay}" IsSynchronizedWithCurrentItem="True">
            .
            .
            .
            </ListView>
        </ScrollViewer>
    </TabItem>
</TabControl>

为什么我的 ObservableCollection<> 列表更新但没有绑定属性

<TextBlock Text="{Binding Path=NumberOfPeople}"/>

? 谁能猜到,或者我错过了一些基本的属性定义?(在 wpf 应用程序中它以这种方式工作)。

编辑

SetField() 实现:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");

    MemberExpression body = selectorExpression.Body as MemberExpression;

    if (body == null)
        throw new ArgumentException("The body must be a member expression");

    OnPropertyChanged(body.Member.Name);
}

protected virtual void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = PropertyChanged;

    if (handler != null) 
        handler(this, new PropertyChangedEventArgs(propertyName));
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;

    field = value;

    OnPropertyChanged(selectorExpression);

    return true;
}       

问候,托马斯

4

2 回答 2

1

1) 检查您的输出窗口是否存在绑定错误。(虽然它们在我看来还不错)

2) My immediate thought would be that PropertyChanged events are not being raised properly by your ViewModel. Can you show the implementation of SetField()? I assume it's some wrapper for removing the magic strings inherent in INotifyPropertyChanged?

于 2012-06-14T07:36:25.020 回答
0

using the dispatcher object, already used to insert item's into the UI bound list, is the key to accomplish this task successfully:

if (DispatcherObject.Thread != Thread.CurrentThread)
    DispatcherObject.Invoke(new Action(() => SetField(ref _numberOfPeople, value, () => NumberOfPeople)));
else
    SetField(ref _numberOfPeople, value, () => NumberOfPeople);
于 2012-06-21T14:27:19.167 回答