用户控件中的依赖项属性仅适用于第一个实例

时间:2012-02-02 21:22:03

标签: c# wpf xaml user-controls dependency-properties

我在窗口中有几个自定义用户控件。它们像工作空间一样动态显示。 我需要在itemscontrol上添加依赖项属性,以便在将项目添加到我的itemscontrol的绑定observable集合时触发向下滚动,如下所示: (用户控件)

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

在我的用户控件的第一次实例化期间,它就像一个魅力。 但是,当动态实例化另一个相同类型的用户控件时,DependencyProperty将永远不再附加到我的scrollviewer。只有第一个实例才能正常工作。 我知道依赖属性是静态的,但这是否意味着它们不能同时在添加到窗口的同一类型的几个用户控件上工作?

更新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()这样的经典方式设置datacontext,那肯定会有效。但是建议使用“共享”视图,那么我们如何使用这种“xaml”设置datacontext的方式使依赖属性工作?

1 个答案:

答案 0 :(得分:0)

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

我启动了Visual C#2010 Express,创建了一个新的“WPF应用程序”,将您的XAML添加到UserControl我想象中标题为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都会向下滚动到底部。

如果您只是在某个用户控件上看到滚动到最底层的行为,那么必定会有一些您没有告诉我们的内容。