简体   繁体   中英

WPF MVVM hierarchy selected item

I am currently implementing the application that displays hierarchy using ListBoxes (please do not suggest using TreeView , ListBoxes are needed).

It looks like that in the article: WPF's CollectionViewSource (with source code) .

在此输入图像描述

Classes:

public class Mountains : ObservableCollection<Mountain>
{
    public ObservableCollection<Lift> Lifts { get; }

    public string Name { get; }
}

public class Lift
{
    public ObservableCollection<string> Runs { get; }
}

The example uses CollectionViewSource instances (see XAML) to simplify the design. An instance of Mountains class is the DataContext for the window.


The problem is: I would like that the Mountains class to have SelectedRun property and it should be set to currently selected run.

public class Mountains : ObservableCollection<Mountain>
{
    public ObservableCollection<Lift> Lifts { get; }

    public string Name { get; }

    public string SelectedRun { get; set; }
}

Maybe I've missed something basic principle, but how can I achieve this?

You may want to read about the use of '/' in bindings. See the section 'current item pointers' on this MSDN article .

Here's my solution:

Xaml

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <TextBlock Margin="5" Grid.Row="0" Grid.Column="0" Text="Mountains"/>
    <TextBlock Margin="5" Grid.Row="0" Grid.Column="1" Text="Lifts"/>
    <TextBlock Margin="5" Grid.Row="0" Grid.Column="2" Text="Runs"/>

    <ListBox Grid.Row="1" Grid.Column="0" Margin="5" 
             ItemsSource="{Binding Mountains}" DisplayMemberPath="Name" 
             IsSynchronizedWithCurrentItem="True" />

    <ListBox Grid.Row="1" Grid.Column="1" Margin="5" 
             ItemsSource="{Binding Mountains/Lifts}" DisplayMemberPath="Name" 
             IsSynchronizedWithCurrentItem="True"/>

    <ListBox Grid.Row="1" Grid.Column="2" Margin="5" 
             ItemsSource="{Binding Mountains/Lifts/Runs}" 
             IsSynchronizedWithCurrentItem="True" 
             SelectedItem="{Binding SelectedRun}"/>
</Grid>

C# (note, you don't need to implement INotifyPropertyChanged unless the properties will be changed and not just selected)

public class MountainsViewModel
{
    public MountainsViewModel()
    {
        Mountains = new ObservableCollection<Mountain>
                        {
                            new Mountain
                                {
                                    Name = "Whistler",
                                    Lifts = new ObservableCollection<Lift>
                                                {
                                                    new Lift
                                                        {
                                                            Name = "Big Red",
                                                            Runs = new ObservableCollection<string>
                                                                       {
                                                                           "Headwall",
                                                                           "Fisheye",
                                                                           "Jimmy's"
                                                                       }
                                                        },
                                                    new Lift
                                                        {
                                                            Name = "Garbanzo",
                                                            Runs = new ObservableCollection<string>
                                                                       {
                                                                           "Headwall1",
                                                                           "Fisheye1",
                                                                           "Jimmy's1"
                                                                       }
                                                        },
                                                    new Lift {Name = "Orange"},
                                                }

                                },
                            new Mountain
                                {
                                    Name = "Stevens",
                                    Lifts = new ObservableCollection<Lift>
                                                {
                                                    new Lift {Name = "One"},
                                                    new Lift {Name = "Two"},
                                                    new Lift {Name = "Three"},
                                                }

                                },
                            new Mountain {Name = "Crystal"},
                        };
    }

    public string Name { get; set; }
    private string _selectedRun;
    public string SelectedRun
    {
        get { return _selectedRun; }
        set
        {
            Debug.WriteLine(value);
            _selectedRun = value;
        }
    }

    public ObservableCollection<Mountain> Mountains { get; set; }
}

public class Mountain
{
    public string Name { get; set; }

    public ObservableCollection<Lift> Lifts { get; set; }
}

public class Lift
{
    public string Name { get; set; }

    public ObservableCollection<string> Runs { get; set; }
}

Here's how I would do it. You want to make sure that you fire the INotifyPropertyChanged event when setting the properties. To get the Selected Run you'll have to get MainViewModel.SelectedMountain.SelectedLift.SelectedRun.

public class MainViewModel: ViewModelBae
{
    ObservableCollection<MountainViewModel> mountains
    public ObservableCollection<MountainViewModel> Mountains
    {
        get { return mountains; }
        set
        {
            if (mountains != value)
            {
                mountains = value;
                RaisePropertyChanged("Mountains");
            }
        }
    }
    MountainViewModel selectedMountain
    public MountainViewModel SelectedMountain
    {
        get { return selectedMountain; }
        set
        {
            if (selectedMountain != value)
            {
                selectedMountain = value;
                RaisePropertyChanged("SelectedMountain");
            }
        }
    }
}

public class MountainViewModel: ViewModelBae
{
    ObservableCollection<LiftViewModel> lifts
    public ObservableCollection<LiftViewModel> Lifts
    {
        get { return lifts; }
        set
        {
            if (lifts != value)
            {
                lifts = value;
                RaisePropertyChanged("Lifts");
            }
        }
    }
    LiftViewModel selectedLift
    public LiftViewModel SelectedLift
    {
        get { return selectedLift; }
        set
        {
            if (selectedLift != value)
            {
                selectedLift = value;
                RaisePropertyChanged("SelectedLift");
            }
        }
    }
}

public class LiftViewModel: ViewModelBae
{
    ObservableCollection<string> runs
    public ObservableCollection<string> Runs
    {
        get { return runs; }
        set
        {
            if (runs != value)
            {
                runs = value;
                RaisePropertyChanged("Runs");
            }
        }
    }
    string selectedRun
    public string SelectedRun
    {
        get { return selectedLift; }
        set
        {
            if (selectedLift != value)
            {
                selectedLift = value;
                RaisePropertyChanged("SelectedLift");
            }
        }
    }
}

<ListBox ItemsSource="{Binding Mountains}" SelectedItem="{Binding SelectedMountain, Mode=TwoWay}">
<ListBox ItemsSource="{Binding SelectedMountain.Lifts}" SelectedItem="{Binding SelectedMountain.SelectedLift, Mode=TwoWay}">
<ListBox ItemsSource="{Binding SelectedMountain.SelectedLift.Runs}" SelectedItem="{Binding SelectedMountain.SelectedLift.SelectedRun, Mode=TwoWay}">

Your ViewModel should not also be a collection, it should contain collections and properties which are bound to the view. SelectedRun should be a property of this ViewModel (MountainViewModel) not Mountains. MountainViewModel should expose the Mountains collection and SelectedRun and should be bound to the listboxes' ItemsSource and SelectedItem.

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