用户控件中的依赖属性仅在第一个实例上工作

4

我在一个窗口中有几个自定义用户控件。它们会动态出现,就像工作区一样。 我需要在itemscontrol上添加一个依赖属性,以便在向绑定的observable集合添加项目时触发下滚,就像这样: (usercontrol)

<ScrollViewer VerticalScrollBarVisibility="Auto" >
<ItemsControl Grid.Row="0" ItemsSource="{Binding Messages}"  View:ItemsControlBehavior.ScrollOnNewItem="True">    
  <ItemsControl.ItemTemplate>
    <DataTemplate>
        <TextBox IsReadOnly="True" TextWrapping="Wrap" Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" />
    </DataTemplate>
  </ItemsControl.ItemTemplate>    
</ItemsControl>
</ScrollViewer>

我的依赖属性代码如下:
    public class ItemsControlBehavior
    {
        static readonly Dictionary<ItemsControl, Capture> Associations =
            new Dictionary<ItemsControl, Capture>();

    public static bool GetScrollOnNewItem(DependencyObject obj)
    {
        return (bool)obj.GetValue(ScrollOnNewItemProperty);
    }

    public static void SetScrollOnNewItem(DependencyObject obj, bool value)
    {
        obj.SetValue(ScrollOnNewItemProperty, value);
    }

    public static readonly DependencyProperty ScrollOnNewItemProperty =
        DependencyProperty.RegisterAttached(
            "ScrollOnNewItem",
            typeof(bool),
            typeof(ItemsControl),
            new UIPropertyMetadata(false, OnScrollOnNewItemChanged));

    public static void OnScrollOnNewItemChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var mycontrol = d as ItemsControl;
        if (mycontrol == null) return;
        bool newValue = (bool)e.NewValue;
        if (newValue)
        {
            mycontrol.Loaded += new RoutedEventHandler(MyControl_Loaded);
            mycontrol.Unloaded += new RoutedEventHandler(MyControl_Unloaded);
        }
        else
        {
            mycontrol.Loaded -= MyControl_Loaded;
            mycontrol.Unloaded -= MyControl_Unloaded;
            if (Associations.ContainsKey(mycontrol))
                Associations[mycontrol].Dispose();
        }
    }

    static void MyControl_Unloaded(object sender, RoutedEventArgs e)
    {
        var mycontrol = (ItemsControl)sender;
        Associations[mycontrol].Dispose();
        mycontrol.Unloaded -= MyControl_Unloaded;
    }

    static void MyControl_Loaded(object sender, RoutedEventArgs e)
    {
        var mycontrol = (ItemsControl)sender;
        var incc = mycontrol.Items as INotifyCollectionChanged;
        if (incc == null) return;
        mycontrol.Loaded -= MyControl_Loaded;
        Associations[mycontrol] = new Capture(mycontrol);
    }

    class Capture : IDisposable
    {
        public ItemsControl mycontrol{ get; set; }
        public INotifyCollectionChanged incc { get; set; }

        public Capture(ItemsControl mycontrol)
        {
            this.mycontrol = mycontrol;
            incc = mycontrol.ItemsSource as INotifyCollectionChanged;
            incc.CollectionChanged +=incc_CollectionChanged;
        }

        void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                  ScrollViewer sv = mycontrol.Parent as ScrollViewer;
                sv.ScrollToBottom();
            }
        }

        public void Dispose()
        {
            incc.CollectionChanged -= incc_CollectionChanged;
        }
    }
}

在我用户控件的首次实例化过程中,它表现得非常出色。但当另一个相同类型的用户控件被动态实例化时,依赖属性就再也不能正确地附加到我的滚动视图器上了。只有第一个实例将正常工作。

我知道依赖属性是静态的,但这是否意味着它们不能同时在添加到窗口的几个相同类型的用户控件上起作用?

更新02/03:这是我如何将viewmodel设置为视图(而不是通过编程):

      <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:testDp.ViewModel" 
    xmlns:View="clr-namespace:testDp.View">


  <DataTemplate DataType="{x:Type vm:ChatTabViewModel}">
<View:ChatTabView />
  </DataTemplate>        
  </ResourceDictionary>

即使在datatemplate标签中设置x:shared = false,它也不能工作。 但是如果我像传统的方式一样设置数据上下文,比如usercontrol.datacontext = new viewmodel(),它肯定会起作用。但是建议使用“共享”视图,那么我们如何让依赖属性与这种“xaml”方式的数据上下文设置方式一起工作呢?

你在DependencyProperty创建时的第二个typeof应该是拥有者类的类型,但你提供了ItemsControl,将其更改为ItemsControlBehavior。但我不认为这是导致问题的原因。 - dowhilefor
我的错...谢谢。不幸的是,问题仍然存在... - KitAndKat
我看到两个作为get/set的静态函数。按照惯例,您为什么不使用属性包装器来处理DependencyProperty呢? - Jimmy
1个回答

0

抱歉,我无法复现您的问题。

我启动了Visual C# 2010 Express,创建了一个新的"WPF应用程序",将您的XAML添加到了一个名为UserControl1的中,并添加了您的ItemsControlBehavior类。然后,我按照以下方式修改了VC#为我创建的MainWindow:

MainWindow.xaml(仅<Window>元素的内容):

<StackPanel Orientation="Vertical">
    <Button Content="Add user control" Click="ButtonAddUserControl_Click" />
    <Button Content="Add message" Click="ButtonAddMessage_Click" />
    <StackPanel Orientation="Horizontal" x:Name="sp" Height="300" />
</StackPanel>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public ObservableCollection<string> Messages { get; private set; }

    public MainWindow()
    {
        InitializeComponent();
        Messages = new ObservableCollection<string>() { "1", "2", "3", "4" };
        DataContext = this;
    }

    private void ButtonAddUserControl_Click(object sender, RoutedEventArgs e)
    {
        sp.Children.Add(new UserControl1());
    }

    private void ButtonAddMessage_Click(object sender, RoutedEventArgs e)
    {
        Messages.Add((Messages.Count + 1).ToString());
    }
}

我没有对你的UserControl中的XAML或者你的ItemsControlBehavior类进行任何修改。

我发现无论添加了多少个用户控件,它们的ScrollViewers都会在我点击“添加消息”按钮时滚动到底部。

如果你只在一个用户控件上看到了滚动到底部的行为,那么肯定有一些你没有告诉我们的东西。


谢谢你的尝试!我会像你一样使用一个非常简单的测试项目,看看它是否能正常工作,然后比较一下我的项目有什么不同,并且我会更新这篇帖子。谢谢。 - KitAndKat
好的,它就是这么简单。我不知道它是否有关系,但在我的真实项目中,我不会以编程方式设置View.DataContext = ViewModel,而是将其放入资源字典中,像这样:<DataTemplate DataType="{x:Type vm:UserControlViewModel}"> <View:UserControl1 /> </DataTemplate>也许它只为几个viewmodel创建一个视图?只有在强制分离实例时才会分开吗? - KitAndKat
@Anna 是的,如果你在XAML中创建它,它将被创建一次并重复使用。你可以使用一个标志来防止共享,即在你的资源上使用x:Shared="false"。 - dowhilefor
我现在正在尝试使用Shared false,但它说资源字典必须编译才能使用。我尝试在字典文件的属性中设置编译操作,但它不再接受我的字典语法了... - KitAndKat
好的,我现在真的相信我应该像这里所说的那样添加x:Shared =“false”http://stackoverflow.com/questions/5900301/why-the-view-constructor-is-not-called-every-time-i-create-a-new-viewmodel 但是它说:“必须编译资源字典”,我找不到解决方法... - KitAndKat
我尝试设置以下内容: <DataTemplate x:Shared="false" DataType="{x:Type vm:ChatTabViewModel}"> <View:ChatTabView /> </DataTemplate> 但是它没有改变任何东西 :( - KitAndKat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接