Значение не попадает в ожидаемый диапазон в ObservableCollection ‹TabItems›

У меня проблема с контролем табуляции.

Я использую привязку и конвертер для динамического добавления элементов в tabcontrol.

При добавлении второго элемента табуляции возникает следующее исключение (см. Код ниже): значение не попадает в ожидаемый диапазон

Его трассировка стека:

в MS.Internal.XcpImports.CheckHResult (UInt32 hr) в MS.Internal.XcpImports.SetValue (IManagedPeerBase obj, свойство DependencyProperty, DependencyObject doh) в MS.Internal.XcpImports.SetValue (IManagedPeperty) .Windows.DependencyObject.SetObjectValueToCore (DependencyProperty dp, значение объекта) в System.Windows.DependencyObject.SetEffectiveValue (свойство DependencyProperty, EffectiveValueEntry & newEntry, Object newValue) в старом System.Windows.DependencyObject операция) в System.Windows.DependencyObject.SetValueInternal (DependencyProperty dp, значение объекта, логическое значение allowReadOnlySet) в System.Windows.Controls.ContentControl.set_Content (значение объекта) в SilverlightApplication1.Services.TabConverter.Convert (значение объекта, тип targetType параметр, культура CultureInfo)

MainPage xaml:

<UserControl x:Class="SilverlightApplication1.MainPage"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         mc:Ignorable="d"
         d:DesignHeight="1024"
         d:DesignWidth="1280"
         xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
         xmlns:local="clr-namespace:SilverlightApplication1.Services">

<UserControl.Resources>
    <local:TabConverter x:Key="tabConverter" />
</UserControl.Resources>

<Grid x:Name="LayoutRoot"
      Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="4*" />
    </Grid.ColumnDefinitions>
    <ListBox SelectionChanged="ListBox_SelectionChanged">
        <ListBoxItem Content="ViewA"></ListBoxItem>
        <ListBoxItem Content="ViewB"></ListBoxItem>
    </ListBox>
    <sdk:TabControl Grid.Column="1"
                    ItemsSource="{Binding Tabs, Converter={StaticResource tabConverter}}" />
</Grid>

Код программной части MainPage:

    public partial class MainPage : UserControl {
    ViewModel viewModel = new ViewModel();

    public MainPage() {
        InitializeComponent();
        this.DataContext = viewModel;
    }

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
        try {
            viewModel.Tabs.Add(new TabItemModel() {
                Header = "New Tab",
                Content = new Grid()
            });

            // sdk:tabcontrol does not listen CollectionChanged of viewModel.Tabs.
            // thats why:
            viewModel.Tabs = viewModel.Tabs;
        } catch (Exception) { }
    }
}

Модель данных:

 public class TabItemModel {
    public string Header { get; set; }
    public UIElement Content { get; set; }
}

Просмотреть модель:

public class ViewModel:INotifyPropertyChanged {
    ObservableCollection<TabItemModel> tabs = new ObservableCollection<TabItemModel>();
    public ObservableCollection<TabItemModel> Tabs {
        get { return tabs; }
        set { tabs = value; OnPropertyChanged(PropertyNames.Tabs); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged(string property) {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    class PropertyNames {
        public const string Tabs = "Tabs";
    }
}

Конвертер вкладок:

public class TabConverter : IValueConverter {

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        try {
            var object_data = value as ObservableCollection<TabItemModel>;
            var result = new List<TabItem>();
            foreach (var item in object_data) {
                result.Add(new TabItem() { 
                    Header = item.Header,
                    Content = item.Content // if comment this, everything works
                });
            }
            return result;
        } catch (Exception e) {
            MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK);
            return null;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        throw new NotImplementedException();
    }
}

person Dhaval Patel    schedule 06.05.2013    source источник
comment
+1 за публикацию рабочего кода, для меня это была простая копипаста :) Я сдался через час, потому что он просто не работает. Imo это ошибочный контроль. Взгляните на ответ @Sphinxxx, я думаю, что вы можете использовать в ссылке Silverlight TabControl с привязкой данных   -  person Silvermind    schedule 07.05.2013


Ответы (1)


Ваша проблема в том, что вы сохраняете UIElement в своей модели данных.

В MVVM это сам по себе большой запрет, но конкретная проблема здесь в том, что когда вы добавляете второй TabItemModel, а конвертер воссоздает все необходимые TabItem, содержимое первого TabItemModel размещается на двух элементах TabItem. сразу на долю секунды. У одного элемента пользовательского интерфейса может быть только один родительский элемент.

Самым «MVVM» решением здесь было бы исключить элементы пользовательского интерфейса из вашей модели данных. Вместо этого просто сохраните часть необработанных данных в TabItemModel.Content и используйте DataTemplate в своем xaml для представления пользовательского интерфейса этих данных.

Если вам действительно необходимо сохранить элементы пользовательского интерфейса в вашей модели данных, я предлагаю вам взглянуть на этот измененный TabControl, который исправляет ошибочный TabControl Silverlight и не требует, чтобы конвертер работал должным образом: Silverlight TabControl с привязкой данных (получено из этого SO сообщение, в котором обсуждается та же проблема: Привязать Silverlight TabControl к коллекции )

person Sphinxxx    schedule 06.05.2013
comment
Просто проверял эту проблему и обнаружил, что свойство ItemTemplate не работает, как можно было бы ожидать от Silverlight sdk TabControl. - person Silvermind; 07.05.2013
comment
@Silvermind: Да, похоже, это так. Две ссылки, которые я разместил в своем ответе, касаются именно этой проблемы (хотя я сам не пробовал настроить TabControl от vorrtex). - person Sphinxxx; 07.05.2013
comment
Я вижу, что проблема с SelectedItem была решена в обновленной статье того же человека: vortexwolf.wordpress.com/2011/07/27/ Похоже, это хороший контроль над тем, что нужно OP. - person Silvermind; 07.05.2013
comment
@DhavalPatel: Надеюсь, это поможет. Если да, примите ответ :) - person Sphinxxx; 07.05.2013