简体   繁体   中英

ItemTemplate for UserControls in ItemsControl - WPF

My task is to implement a MDI-like interface in our WPF app.

I have created this simple class as a base for all the views:

public class BaseView : UserControl, INotifyPropertyChanged
{
        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string? name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        private ViewType _type = ViewType.Null;
        private string _tabTitle = string.Empty;
        private bool _isSelected = false;

        public ViewType Type { get => _type; set { _type = value; OnPropertyChanged(); } }
        public string TabTitle { get => _tabTitle; set { _tabTitle = value; OnPropertyChanged(); } }
        public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } }
}

Next, I created few test Views. All of them start like this: <local:BaseView...

In main window, there are two controls: ItemsControl (for displaying the list of opened views), and ContentControl (for displaying the selected view.)

I store all the opened views in a ObservableCollection: ObservableCollection<BaseView>... . I wanted to display them as a list, so I created ItemsControl:

<ItemsControl x:Name="mainItemsControl">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Padding="2" Margin="2" Tag="{Binding Type}">
                <TextBlock Text="{Binding TabTitle}" Foreground="White"/>
                        </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

When I set the ItemsControl's source ( mainItemsControl.ItemsSource = openedViews; ) and started the application, ItemsControl displayed the content of each View instead of the ItemTemplate (Border with the TextBlock). What did I do wrong?

If I understood correctly, then the openedViews collection consists of BaseView. If so, then BaseView is a UIElement.
But Data Templates are used to render non UIElements.
If the Content receives a UIElement, then it is rendered directly as is.

One possible variant solution.
You need to remove the INotifyPropertyChanged interface from BaseView. Create a data source for your BaseView with an implementation of INotifyPropertyChanged.
In BaseView create DependencyProperty for this source.

Create a simple, helper container for the openedViews collection.
Something like this (pseudo code):

public class SomeContainer 
{
    public BaseDataSource DataSource 
    {
        get => _dataSource;
        set
        {
            _dataSource = null;
            if(View is not null)
            {
                View.DataSource = DataSource;
            }            
        }
    }
    public BaseView View
    {
        get => _view;
        set
        {
            _view = value;
            if(_view is not null)
            {
                _view.DataSource = DataSource;
            }            
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM