Присоединенное свойство прикреплено только в первом экземпляре пользовательского элемента управления - PullRequest
0 голосов
/ 06 февраля 2012

Следуя примеру Джоша Смита на рабочих пространствах mvvm (представление клиентов), у меня есть главное окно и основная модель окна просмотра, которая содержит ObservableCollection «ChatTabViewModel»:

internal class FriendsListViewModel : ObservableObject
{
    #region bound properties
    private ICollectionView viewfriends;
    private ObservableCollection<ChatTabViewModel> _chatTab; 
    ...
    #endregion
}

У меня есть область, посвященная этой коллекции в xaml, вот так:

<ContentControl Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Content="{Binding Path=ChatTabs}" ContentTemplate="{StaticResource ChatTabsTemplate}" />

И в моем словаре ресурсов:

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

<DataTemplate x:Key="ClosableTabItemTemplate">
    <DockPanel>
      <Button
        Command="{Binding Path=CloseCommand}"
        Content="X"
        Cursor="Hand"
        DockPanel.Dock="Right"
        Focusable="False"
        FontFamily="Courier"
        FontSize="9"
        FontWeight="Bold"
        Margin="0,1,0,0"
        Padding="0"
        VerticalContentAlignment="Bottom"
        Width="16" Height="16"
        />
      <ContentPresenter
        Content="{Binding Path=Caption, Mode=OneWay}"
        VerticalAlignment="Center">
      </ContentPresenter>
    </DockPanel>
</DataTemplate>

<DataTemplate x:Key="ChatTabsTemplate">
    <TabControl
      IsSynchronizedWithCurrentItem="True"
      ItemsSource="{Binding}"
      ItemTemplate="{StaticResource ClosableTabItemTemplate}"
      Margin="4"/>
</DataTemplate>

При пользовательском событии я добавляю новый ChattabViewModel в свою коллекцию, и связанный с ним вид отображается в главном окне.

Но когда я попытался добавить прикрепленное свойство на полосу прокрутки в ChattabView, это свойство будет прикреплено только к первому экземпляру ChattabViewModel, другие вкладки не будут привязаны к присоединенному свойству. Вот ChattabView XAML:

 <ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="0">
  <ItemsControl 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>

и код прикрепленного объекта недвижимости:

namespace GtalkOntre.View
{
    /// <summary>
    /// Util class to scroll down when a new message is added.
    /// </summary>
    /// <remarks>attached property called ScrollOnNewItem that when set to true hooks into the INotifyCollectionChanged events of the itemscontrol items source and upon detecting a new item, scrolls the scrollbar to it.</remarks>
    public class ItemsControlBehavior
    {
        static 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 DependencyProperty ScrollOnNewItemProperty =
            DependencyProperty .RegisterAttached(
                "ScrollOnNewItem",
                typeof(bool),
                typeof(ItemsControlBehavior),
                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 += MyControl_Loaded;
                mycontrol.Unloaded += 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;
            }
        }
    }
}

Так почему присоединенное свойство связывается только один раз, при первом появлении "chattabview" коллекции chattabviewmodel? и, следовательно, работает только над первой моделью чата. Когда я закрою их все, присоединенное свойство будет отменено в последнем экземпляре chattabviewmodel, а когда я добавлю новую первую chattabviewmodel, свойство будет привязано правильно. Таким образом, он запускается только в первом и последнем экземпляре коллекции "chattabviewmodel" mainwindowviewmodel.

После недели поисков я немного отчаялся ...

Пока что моя гипотеза такова: проблема может быть связана с тем, как я устанавливаю представление для моей модели представления в словарных ресурсах. Представление может быть общим и только первая полоса прокрутки может реагировать. Я попытался добавить атрибут x:Shared = false в тег DataTemplate, но он ничего не изменил.

Ответы [ 2 ]

2 голосов
/ 06 февраля 2012

Вы уверены, что существуют различные случаи создания вашего ChatTabView?

Я считаю, что WPF TabControl повторно использует существующий шаблон, если он тот же, вместо создания нового, и просто заменяет DataContext позади него.

Таким образом, будет создана только одна копия ваших ChatTabView, и переключение вкладок заменяет DataContext позади ChatTabView на другой элемент в коллекции.

2 голосов
/ 06 февраля 2012

Вы не показали нам ChatTabsTemplate, поэтому я могу только предположить, что он содержит TabControl. Если так, то это объясняет поведение, которое вы видите. TabControl лениво загружает свои дочерние элементы вкладки, поэтому будет инициализирован только текущий вид, и, следовательно, к нему будет применено присоединенное свойство. Однако, когда вы переключаете вкладки, вы должны увидеть то же срабатывание вложенного свойства. Разве это не так?

Что касается твоей догадки, это не совсем верно. DataTemplate является общим для , но DataTemplate используется для создания отдельных экземпляров его содержимого, которые не являются общими.

...