none
MVVM : TreeView Stye Trigger nur bei bestimmtem Typ RRS feed

  • Frage

  • Hallo Leute, dank Peter habe ich nun das templating eines TreeView gelöst

    Folgendes Szenario : treeView , welcher je anch Typ des Models entweder Checkboxen oder nur einen texBlock anzeigt. Sobald Checkboxen angezeigt werden , darf nur eine Checbox prop RootNode angehakt werden , Rest wird Disabled

    Hier der View :

    <Grid DataContext="{Binding Source={StaticResource vm}}">
            <Grid.RowDefinitions>
                <RowDefinition Height="196*" />
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <TreeView ItemsSource="{Binding Items}">
                <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        
                        <Setter Property="IsSelected" Value="{Binding Path=IsSelected}"/>
                        <Style.Triggers>
                            <Trigger Property="HasItems" Value="true">
                                <Setter Property="Focusable" Value="False"/>
                            </Trigger>         
                        </Style.Triggers>
                    </Style>
                </TreeView.ItemContainerStyle>
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type model:RootNode}" ItemsSource="{Binding Path=SubNodes}">
                        <Grid>
                            <TextBlock Text="{Binding Text}" />
                        </Grid>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type model:SubNodeItemWithCheckbox}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="16"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <CheckBox Grid.Column="0" IsChecked="{Binding IsSelected, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
                            <TextBlock Text="{Binding Text}" Grid.Column="1"/>
                        </Grid>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type model:SubNode}">
                            <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
    
    
            <Button Grid.Row="1" Width="30" Command="{Binding OkCommand}">
                <Image Source="/Images/Sign001.ico"/>
            </Button>
        </Grid>
    

    Und das Model :

        public class SubNodeItemWithCheckbox:INode
        {
            public SubNodeItemWithCheckbox(RootNode parent)
            {
                _parent = parent;
            }
    
            private RootNode _parent;
    
            private bool _isEnabled;
            public bool IsEnabled
            {
                get { return _isEnabled; }
                set { _isEnabled = value;OnPropertyChanged(() => IsEnabled); }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set
                { 
                    _isSelected = value;
                    ChangeEnabledStatus();
                    OnPropertyChanged(() => IsSelected); 
                }
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value;OnPropertyChanged(() =>  Text); }
            }
    
            private ExtendedData _extData;
    
            public RootNode Parent
            {
                get { return _parent; }
            }
    
            public ExtendedData ExtData
            {
                get { return _extData; }
                set { _extData = value; OnPropertyChanged(() => ExtData); }
            }
    
            private void ChangeEnabledStatus()
            {
                foreach (var subNode in _parent.SubNodes.Where(p => p.Text != _text))
                {
                    subNode.IsEnabled = !_isSelected;
                }
            }
    


    Jetzt das Problem : Sobald ich eine Checkbox anhake, disabled er die Checkboxen , OK , sobald ich aber nun eine andere Checkbox anhake , aus einem anderem Root , wir die alte Checkbox disabled und auf IsSelected auf False gesetzt, logisch mein trigger besagt auch , das er das daran Binden soll. Wie kann ich dem Trigger nun sagen, dass er nur gültig für ein gewisses template ist ?

    Grüße

    Dienstag, 6. September 2011 08:29

Antworten

  • Also ich habe es MVVM konform gelöst ;-) : Das TreeViewControl

    <UserControl x:Class="MK.Client.View.KlassifizierungsTreeView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:vm="clr-namespace:MK.Client.ViewModel">
        <UserControl.DataContext>
            <vm:KlassifizierzungsTreeViewModel/>
        </UserControl.DataContext>
        <UserControl.Resources>
            <Style TargetType="{x:Type TreeViewItem}" x:Key="WithCheckbox">
                <Style.Triggers>
                    <Trigger Property="HasItems" Value="True">
                        <Setter Property="Focusable" Value="False" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <Style TargetType="{x:Type TreeViewItem}" x:Key="Normal">
                <Style.Triggers>
                    <Trigger Property="HasItems" Value="True">
                        <Setter Property="Focusable" Value="False" />
    
                    </Trigger>
                    <Trigger Property="HasItems" Value="False">
                        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </UserControl.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="555*"/>
                <RowDefinition Height="40*"/>
            </Grid.RowDefinitions>
            <TreeView ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource ResourceKey=WithCheckbox}" Grid.Row="0">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type vm:RootNodeModelViewModel}" ItemsSource="{Binding SubNodes}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:RootNodeCheckboxModelViewModel}" ItemsSource="{Binding SubNodes}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:SubNodeModelViewModel}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:SubNodeChecbkoxModelViewModel}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="17"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}" Grid.Column="0" IsEnabled="{Binding IsEnabled,Mode=TwoWay}"/>
                            <TextBlock Text="{Binding Text}" Grid.Column="1"/>
                        </Grid>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
            <Button Command="{Binding SaveCommand}" Grid.Row="1" Width="40">
                <Image Source="/MK.Client;component/Images/Sign001.ico" Stretch="Fill"/>
            </Button>
        </Grid>
    </UserControl>
    
    

        public class KlassifizierzungsTreeViewModel:BaseViewModel
        {
            public enum ResourceKeys
            {
                Normal,
                WithCheckbox
            }
    
    
            public KlassifizierzungsTreeViewModel()
            {
                Items = new ObservableCollection<IRootNodeViewModelBase>(VMConverter.GetViewModels(DatabaseLoader.GetNodes()));
            }
    
            private ObservableCollection<IRootNodeViewModelBase> _items;
            public ObservableCollection<IRootNodeViewModelBase> Items
            {
                get { return _items; }
                set { _items = value;OnPropertyChanged(() => Items); }
            }
    
            private ICommand _saveCommand;
            public ICommand SaveCommand
            {
                get { return _saveCommand ?? (_saveCommand = new DelegateCommand(() => { })); }
            }
    
            private void SaveViewModelsToDatabase()
            {
               
            }
    
            public string ResourceKeyString { get { return ResourceKeys.Normal.ToString(); } }
        }
    

    Dann die einzelnen ViewModels für den tree :

        public class RootNodeModelViewModel : BaseViewModel, IRootNodeViewModelBase
        {
            private RootNode _root;
            public RootNodeModelViewModel(RootNode root)
            {
                _root = root;
                Text = root.Text;
                List<INodeViewViewModelBase> nodeViews = new List<INodeViewViewModelBase>();
                foreach (Node node in root.SubNodes)
                {
                    var model = new SubNodeChecbkoxModelViewModel(node, this);
                    nodeViews.Add(model);
                }
                
                 SubNodes = new ObservableCollection<INodeViewViewModelBase>(nodeViews);
            }
    
            private ObservableCollection<INodeViewViewModelBase> _subNodes;
    
            public RootNode Root
            {
                get { return _root; }
                set { _root = value;OnPropertyChanged(() => Root); }
            }
    
            public ObservableCollection<INodeViewViewModelBase> SubNodes
            {
                get { return _subNodes; }
                set { _subNodes = value;OnPropertyChanged(() => SubNodes); }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set { _isSelected = value;
                    _root.IsSelected = value;OnPropertyChanged(() => IsSelected);
                }
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value;OnPropertyChanged(() => Text); }
            }
        }
    


    Und die Basis für die Nodes als ViewModel :

    public class TreeViewNodeModelBaseModel : INotifyPropertyChanged,Interface.INodeViewViewModelBase
        {
    
            public TreeViewNodeModelBaseModel(Node node,IRootNodeViewModelBase parent)
            {
                Node = node;
                RootModelView = parent;
                IsEnabled = true;
            }
    
            public Node Node { get; set; }
    
            private bool _isEnabled;
            public bool IsEnabled
            {
                get { return _isEnabled; }
                set { _isEnabled = value; OnPropertyChanged(() => IsEnabled); }
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value; OnPropertyChanged(() => Text); }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set { _isSelected = value;
                Node.IsSelected = value;
                    DisabledUnselectedNodes(); OnPropertyChanged(() => IsSelected);
                }
            }
    
            private void DisabledUnselectedNodes()
            {
                foreach (INodeViewViewModelBase node in RootModelView.SubNodes)
                {
                    if (node.Text != Node.Text)
                        node.IsEnabled = !this.IsSelected;
                }
            }
    
            public IRootNodeViewModelBase RootModelView { get; set; }
    
            protected void OnPropertyChanged<T>(Expression<Func<T>> action)
            {
                var propertyName = GetPropertyName(action);
                OnPropertyChanged(propertyName);
            }
    
            private static string GetPropertyName<T>(Expression<Func<T>> action)
            {
                var expression = (MemberExpression)action.Body;
                var propertyName = expression.Member.Name;
                return propertyName;
            }
    
            protected void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    var e = new PropertyChangedEventArgs(propertyName);
                    handler(this, e);
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
    
        }
    

    Ich hatte massiv Probleme zu sehen , dass ein ViewModel ein Wrapper um ein Model ist und die View eben nichts vom Model wissen darf.

    So klappts aber nu !

    Grüße

    • Als Antwort markiert Pawel Warmuth Mittwoch, 7. September 2011 13:39
    Mittwoch, 7. September 2011 13:39

Alle Antworten

  • Evtl. würde ich das über die Datenquelle lösen und dabei MultiDataTrigger verwenden:

    http://msdn.microsoft.com/en-us/library/system.windows.multidatatrigger.aspx

     

    Dienstag, 6. September 2011 08:46
    Beantworter
  • Evtl. würde ich das über die Datenquelle lösen und dabei MultiDataTrigger verwenden:

    http://msdn.microsoft.com/en-us/library/system.windows.multidatatrigger.aspx

     

    Dienstag, 6. September 2011 08:47
    Beantworter
  • Klingt zwar gut, leider finde ich kein passendes beispiel welches nur annähernd mein problem darstellt.

    Muss ich als Condition SourceName meinen Namen der Klasse angeben ?

    Grüße

    Dienstag, 6. September 2011 09:04
  • Also ich habe es MVVM konform gelöst ;-) : Das TreeViewControl

    <UserControl x:Class="MK.Client.View.KlassifizierungsTreeView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:vm="clr-namespace:MK.Client.ViewModel">
        <UserControl.DataContext>
            <vm:KlassifizierzungsTreeViewModel/>
        </UserControl.DataContext>
        <UserControl.Resources>
            <Style TargetType="{x:Type TreeViewItem}" x:Key="WithCheckbox">
                <Style.Triggers>
                    <Trigger Property="HasItems" Value="True">
                        <Setter Property="Focusable" Value="False" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <Style TargetType="{x:Type TreeViewItem}" x:Key="Normal">
                <Style.Triggers>
                    <Trigger Property="HasItems" Value="True">
                        <Setter Property="Focusable" Value="False" />
    
                    </Trigger>
                    <Trigger Property="HasItems" Value="False">
                        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </UserControl.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="555*"/>
                <RowDefinition Height="40*"/>
            </Grid.RowDefinitions>
            <TreeView ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource ResourceKey=WithCheckbox}" Grid.Row="0">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type vm:RootNodeModelViewModel}" ItemsSource="{Binding SubNodes}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:RootNodeCheckboxModelViewModel}" ItemsSource="{Binding SubNodes}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:SubNodeModelViewModel}">
                        <TextBlock Text="{Binding Text}"/>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type vm:SubNodeChecbkoxModelViewModel}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="17"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}" Grid.Column="0" IsEnabled="{Binding IsEnabled,Mode=TwoWay}"/>
                            <TextBlock Text="{Binding Text}" Grid.Column="1"/>
                        </Grid>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
            <Button Command="{Binding SaveCommand}" Grid.Row="1" Width="40">
                <Image Source="/MK.Client;component/Images/Sign001.ico" Stretch="Fill"/>
            </Button>
        </Grid>
    </UserControl>
    
    

        public class KlassifizierzungsTreeViewModel:BaseViewModel
        {
            public enum ResourceKeys
            {
                Normal,
                WithCheckbox
            }
    
    
            public KlassifizierzungsTreeViewModel()
            {
                Items = new ObservableCollection<IRootNodeViewModelBase>(VMConverter.GetViewModels(DatabaseLoader.GetNodes()));
            }
    
            private ObservableCollection<IRootNodeViewModelBase> _items;
            public ObservableCollection<IRootNodeViewModelBase> Items
            {
                get { return _items; }
                set { _items = value;OnPropertyChanged(() => Items); }
            }
    
            private ICommand _saveCommand;
            public ICommand SaveCommand
            {
                get { return _saveCommand ?? (_saveCommand = new DelegateCommand(() => { })); }
            }
    
            private void SaveViewModelsToDatabase()
            {
               
            }
    
            public string ResourceKeyString { get { return ResourceKeys.Normal.ToString(); } }
        }
    

    Dann die einzelnen ViewModels für den tree :

        public class RootNodeModelViewModel : BaseViewModel, IRootNodeViewModelBase
        {
            private RootNode _root;
            public RootNodeModelViewModel(RootNode root)
            {
                _root = root;
                Text = root.Text;
                List<INodeViewViewModelBase> nodeViews = new List<INodeViewViewModelBase>();
                foreach (Node node in root.SubNodes)
                {
                    var model = new SubNodeChecbkoxModelViewModel(node, this);
                    nodeViews.Add(model);
                }
                
                 SubNodes = new ObservableCollection<INodeViewViewModelBase>(nodeViews);
            }
    
            private ObservableCollection<INodeViewViewModelBase> _subNodes;
    
            public RootNode Root
            {
                get { return _root; }
                set { _root = value;OnPropertyChanged(() => Root); }
            }
    
            public ObservableCollection<INodeViewViewModelBase> SubNodes
            {
                get { return _subNodes; }
                set { _subNodes = value;OnPropertyChanged(() => SubNodes); }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set { _isSelected = value;
                    _root.IsSelected = value;OnPropertyChanged(() => IsSelected);
                }
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value;OnPropertyChanged(() => Text); }
            }
        }
    


    Und die Basis für die Nodes als ViewModel :

    public class TreeViewNodeModelBaseModel : INotifyPropertyChanged,Interface.INodeViewViewModelBase
        {
    
            public TreeViewNodeModelBaseModel(Node node,IRootNodeViewModelBase parent)
            {
                Node = node;
                RootModelView = parent;
                IsEnabled = true;
            }
    
            public Node Node { get; set; }
    
            private bool _isEnabled;
            public bool IsEnabled
            {
                get { return _isEnabled; }
                set { _isEnabled = value; OnPropertyChanged(() => IsEnabled); }
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value; OnPropertyChanged(() => Text); }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set { _isSelected = value;
                Node.IsSelected = value;
                    DisabledUnselectedNodes(); OnPropertyChanged(() => IsSelected);
                }
            }
    
            private void DisabledUnselectedNodes()
            {
                foreach (INodeViewViewModelBase node in RootModelView.SubNodes)
                {
                    if (node.Text != Node.Text)
                        node.IsEnabled = !this.IsSelected;
                }
            }
    
            public IRootNodeViewModelBase RootModelView { get; set; }
    
            protected void OnPropertyChanged<T>(Expression<Func<T>> action)
            {
                var propertyName = GetPropertyName(action);
                OnPropertyChanged(propertyName);
            }
    
            private static string GetPropertyName<T>(Expression<Func<T>> action)
            {
                var expression = (MemberExpression)action.Body;
                var propertyName = expression.Member.Name;
                return propertyName;
            }
    
            protected void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    var e = new PropertyChangedEventArgs(propertyName);
                    handler(this, e);
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
    
        }
    

    Ich hatte massiv Probleme zu sehen , dass ein ViewModel ein Wrapper um ein Model ist und die View eben nichts vom Model wissen darf.

    So klappts aber nu !

    Grüße

    • Als Antwort markiert Pawel Warmuth Mittwoch, 7. September 2011 13:39
    Mittwoch, 7. September 2011 13:39