[Version 2.0] MVVM Binding source in a LayoutAnchorablePane

May 10, 2012 at 8:08 PM

I have a viewmodel (type X) of a part of the application and I have its DataTemplate as a resource, the DataTemplate for this that is a tabcontrol with the binding source binded to a list of other viewModels child (type Y), I'd like to create this datatemplate as a LayoutAnchorablePane in which the list of child viewmodels can be binded as the itemsSource for the tabControl. Then I'll have to put the viewModel X as content of a LayoutAnchorablePaneGroup, and have the same layout of the left side as reported in the sample code. Is it possible to have ItemsControl of type Anchorable and Document (and their corrensponding groups) to use for binding to list of viewModels? Now it's possible to bind only the dockingManager to a list of document and to a list of anchorable, but in this way there isn't the possibility to have viewModel decompositions and a custom layout for each element. 

 

<DataTemplate DataType="{x:Type local:ViewModel_X}">
    <avalonDock:LayoutAnchorablePane ItemsSource="{Binding ListOfViewModels_Y}">
    </avalonDock:LayoutAnchorablePane>
</DataTemplate>

....
        <avalonDock:DockingManager >
            <avalonDock:LayoutRoot>
                <avalonDock:LayoutPanel Orientation="Horizontal">
                    <avalonDock:LayoutAnchorablePaneGroup DockWidth="200">
                        <avalonDock:LayoutAnchorablePane>
                            <avalonDock:LayoutAnchorable Title="Panel 1">
                                <Border Background="Cyan"/>
                            </avalonDock:LayoutAnchorable>
                            <avalonDock:LayoutAnchorable Title="Panel 2">
                                <Border Background="Green"/>
                            </avalonDock:LayoutAnchorable>
                        </avalonDock:LayoutAnchorablePane>
                    </avalonDock:LayoutAnchorablePaneGroup>

                    <avalonDock:LayoutAnchorablePaneGroup DockWidth="200" >
                        <ContentControl Content="{Binding InstanceOfViewModel_X}"
                    </avalonDock:LayoutAnchorablePaneGroup>
                </avalonDock:LayoutPanel>
            </avalonDock:LayoutRoot>
        </avalonDock:DockingManager>

May 12, 2012 at 12:18 PM

I'm not sure I've correctly understood what your are tring to achive, anyway I can suggest that you should:

1) Binding the DocumentsSource and AnchorableSource property to corresponding ViewModel properties.
2) Use the LayoutItemTemplate property to describe the view for your view models.
3) Use the LayoutItemContainerStyle to provide binding to properties like the Title,Tooltip and so on, and most important in your scenario the ContentId
4) Describe your custom initial layout taking care of setting the right ContentId properties so that AvalonDock can find the right content from AnchorablesSource and DocumentsSource lists.

Finally please get a look at the MVVMTestApp sample application to see how MVVM works in AD.

May 12, 2012 at 4:05 PM

I have a mainViewModel with different Properties, GroupOfItems and GroupOfPersons

 

    public class MainViewModel : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private ItemGroup _groupOfItems;
        public ItemGroup GroupOfItems
        {
            get { return _groupOfItems; }
            set
            {
                if (_groupOfItems != value)
                {
                    _groupOfItems = value;
                    NotifyPropertyChanged("GroupOfItems");
                }
            }
        }

        private PersonGroup _groupOfPersons;
        public PersonGroup GroupOfPersons
        {
            get { return _groupOfPersons; }
            set
            {
                if (_groupOfPersons != value)
                {
                    _groupOfPersons = value;
                    NotifyPropertyChanged("GroupOfPersons");
                }
            }
        }


        public MainViewModel()
        {
            _groupOfItems = new ItemGroup();
            _groupOfPersons = new PersonGroup();
        }
    }

 

each  property can be displayed as a list of items

    public class ItemGroup : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private IList<Iteml> _listOfItems;
        public IList<Iteml> ListOfItems
        {
            get { return _listOfItems; }
            set
            {
                if (_listOfItems != value)
                {
                    _listOfItems = value;
                    NotifyPropertyChanged("ListOfItems");
                }
            }
        }

        public ItemGroup()
        {
            _listOfItems = new List<Iteml>();
            _listOfItems.Add(new Iteml() { KeyValue = "A2" });
            _listOfItems.Add(new Iteml() { KeyValue = "B2" });
            _listOfItems.Add(new Iteml() { KeyValue = "C2" });
        }
    }

 

 

    public class PersonGroup: INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private IList<Person> _listOfPersons;
        public IList<Person> ListOfPersons
        {
            get { return _listOfPersons; }
            set
            {
                if (_listOfPersons != value)
                {
                    _listOfPersons = value;
                    NotifyPropertyChanged("ListOfPersons");
                }
            }
        }

        public PersonGroup()
        {
            _listOfPersons = new List<Person>();
            _listOfPersons.Add(new Person() { Name = "John" });
            _listOfPersons.Add(new Person() { Name = "Mario" });
            _listOfPersons.Add(new Person() { Name = "Lewis" });
        }
    }

 

the single items has a simple viewmodel

 

    public class Iteml : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private string _keyValue;
        public string KeyValue
        {
            get { return _keyValue; }
            set
            {
                if (_keyValue != value)
                {
                    _keyValue = value;
                    NotifyPropertyChanged("KeyValue");
                }
            }
        }


    }

 

 

    public class Person : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        // Declare the PropertyChanged event
        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event passing the
        // source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }
    }

 

the DataTemplate of each group is a TabControl binded to the collections and the layout is

 <Grid>
        <Grid.Resources>
            <DataTemplate DataType="{x:Type local:Iteml}">
                <TextBlock Text="{Binding KeyValue}" Background="Yellow"/>
            </DataTemplate>        
            <DataTemplate DataType="{x:Type local:ItemGroup}">
                <TabControl ItemsSource="{Binding ListOfItems}"/>
            </DataTemplate>

            <DataTemplate DataType="{x:Type local:Person}">
                <TextBlock Text="{Binding Name}" Background="Cyan"/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:PersonGroup}">
                <TabControl ItemsSource="{Binding ListOfPersons}"/>
            </DataTemplate>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <ContentControl Grid.Column="0" Content="{Binding GroupOfItems}"/>
        <ContentControl Grid.Column="1" Content="{Binding GroupOfPersons}"/>
    </Grid>

 

I'd like to use avalondock having all the tabItems as anchorables, but in the Avalondock sample There is only one collection of Anchorables binded to the docking manager, is it possible to have instead the possibility to have each anchorable or anchorable group as an itemscontrol that can be binded to a collection of anchorable? In this case I'd like to have this condition

 

 

    <Grid>
        <Grid.Resources>
            <DataTemplate DataType="{x:Type local:Iteml}">
                <avalonDock:LayoutDocumentPane>
                    <TextBlock Text="{Binding KeyValue}" Background="Yellow"/>
                </avalonDock:LayoutDocumentPane>
            </DataTemplate>        
            <DataTemplate DataType="{x:Type local:ItemGroup}">
                <avalonDock:LayoutDocumentPaneGroup ItemsSource="{Binding ListOfItems}"/>
            </DataTemplate>

            <DataTemplate DataType="{x:Type local:Person}">
                <avalonDock:LayoutDocumentPane>
                    <TextBlock Text="{Binding Name}" Background="Cyan"/>
                </avalonDock:LayoutDocumentPane>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:PersonGroup}">
                <avalonDock:LayoutDocumentPaneGroup ItemsSource="{Binding ListOfPersons}"/>
            </DataTemplate>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <avalonDock:DockingManager Grid.Column="0">
            <avalonDock:LayoutRoot>
                <avalonDock:LayoutPanel>
                    <AnchorableContentControl Content="{Binding GroupOfItems}"/>
                    <AnchorableContentControl Content="{Binding GroupOfPersons}"/>
                </avalonDock:LayoutPanel>
            </avalonDock:LayoutRoot>
        </avalonDock:DockingManager>
    </Grid>

How can do this? Do you think is a simple way, or a possible develop for doing this?