Доступ к вложенной коллекции свойств из ViewModel - PullRequest
0 голосов
/ 03 января 2019

У меня есть пользовательский элемент управления LookupPanelView, который состоит из TextBox и ListBox.Он имеет прикрепленное свойство ItemsSource, к которому привязывается ListBox, поэтому привязанные данные могут быть установлены вне элемента управления.

LookupPanelView

public partial class LookupPanelView : UserControl
{
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(LookupPanelView));

    public IEnumerable ItemsSource
    {
        get => (IEnumerable)GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }

    public LookupPanelView()
    {
        InitializeComponent();
    }
}

ItemsSource элемента управления привязан к свойству в моей основной ViewModel, который решает, какие данные отображать.

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<DomainObject> LookupPanelItems { get; private set; }

    public MainViewModel()
    {
        LookupPanelItems = // Fetch the data to display in the control.
    }
}

<Window x:Class="MainWindow"
    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"
    UseLayoutRounding="True">
<Grid>
    <lookupPanelView:LookupPanelView Grid.Column="0" ItemsSource="{Binding LookupPanelItems}"/>
</Grid>

Я хотел бы расширить пользовательский элемент управления доесть функция поиска, где вы вводите TextBox, и он выбирает соответствующий элемент из ListBox.Эта логика должна содержаться в элементе управления, так как он должен знать, как искать свои собственные элементы.Я думаю, что мне нужно предоставить элемент управления собственный ViewModel для хранения логики, но тогда как мне получить доступ к вложенному свойству ItemsSource в ViewModel для поиска элементов?Я бы хотел как можно больше избегать использования выделенного кода для удобства сопровождения и тестирования.

Ответы [ 3 ]

0 голосов
/ 03 января 2019

Эта логика должна содержаться в элементе управления, поскольку он должен знать, как искать свои собственные элементы.

Зачем вам тогда модель представления? Если «логика должна содержаться в элементе управления», то реализуйте ее там.

Я думаю, мне нужно предоставить элементу управления свой собственный ViewModel для хранения логики, но тогда как мне получить доступ к прикрепленному свойству ItemsSource в ViewModel для поиска элементов?

Это противоречит вашему первому предложению, но если элементу управления по какой-то причине действительно нужна собственная модель представления, а модели представления необходим доступ к элементу управления, вы можете просто вставить ссылку на элемент управления при создании представления. модель, например:

public LookupPanelView()
{
    InitializeComponent();
    this.DataContext = new ViewModel(this);
}

Но, вероятно, вам нужно создать собственный элемент управления с шаблоном по умолчанию. Это просто класс, унаследованный от Control и не имеющий кода кода или файла XAML. Пожалуйста, обратитесь к этому учебнику для примера. UserControl больше похоже на составное представление, чем на собственный элемент управления со своей собственной логикой.

0 голосов
/ 03 января 2019

Подумав немного, я придумал для вас такую ​​отправную точку.
Сначала вы создаете свой элемент управления примерно так:

<UserControl x:Class="SO_App.UC.SearchableListView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:SO_App.UC"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="root"><!-- This allows us to keep the Data Context inheritance -->
    <Grid.Resources>
        <CollectionViewSource Source="{Binding ItemsSource}" x:Key="Items"/> <!-- This is for us to use Filtering and so on -->
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBox x:Name="txtSearch" Text="{Binding SearchTerm}"/>
    <!-- Placeholder -->
    <TextBlock IsHitTestVisible="False" Text="{Binding SearchTextPlaceHolder,TargetNullValue=Search, FallbackValue=Search}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" Foreground="DarkGray">
        <TextBlock.Style>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="Visibility" Value="Collapsed"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Text, ElementName=txtSearch}" Value="">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
    <ListView x:Name="lstItems" Grid.Row="1" ItemsSource="{Binding Source={StaticResource Items}}"/>
</Grid>


Корневой элемент сохраняет Bidningпользовательский элемент управления в такте, в то время как мы можем использовать обычную привязку от родительского элемента в нашем главном окне.Тогда в вашем MainWindow.xaml вы бы использовали его так:

<Window x:Class="SO_App.MainWindow"
    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"
    xmlns:vm="clr-namespace:VM;assembly=VM"
    xmlns:model="clr-namespace:Model;assembly=Model"
    xmlns:converter="clr-namespace:SO_App.Converters"
    xmlns:uc="clr-namespace:SO_App.UC"
    xmlns:local="clr-namespace:SO_App"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <vm:MainViewModel/>
</Window.DataContext>
<Grid>
    <uc:SearchableListView SearchTextPlaceHolder="Search" ItemsSource="{Binding Users}">
        <uc:SearchableListView.Resources>
            <DataTemplate DataType="{x:Type model:User}">
                <Grid>
                    <StackPanel>
                        <TextBlock Text="{Binding ID}"/>
                        <TextBlock Text="{Binding Name}"/>
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </uc:SearchableListView.Resources>
    </uc:SearchableListView>
</Grid>


Ради этого поста здесь есть ViewModel:

public class MainViewModel : BaseViewModel
{
    public MainViewModel()
    {
        Users = new List<User>();
        for (int i = 0; i < 6; i++)
        {
            Users.Add(new User
            {
                ID = i,
                Name = $"John the {i + 1}",
                State = i % 2 == 0 ? "CA" : "IL",
                Cases = new List<Case>() { new Case { CaseID = (i + 1) * 10, Vendor = ((i + 1) * 10) - 2 }, new Case { CaseID = (i + 1) * 10, Vendor = ((i + 1) * 10) - 2 } }
            });
        }
    }
}  

А вотпользовательский объект:

namespace Model
{
    public class User//Ideally you would have INPC implemented here
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string State { get; set; }
        public List<Case> Cases { get; set; }
    }
}  

Надеюсь, что это даст вам достаточно информации, чтобы начать реализацию в правильном направлении и с как можно большим количеством MvvM.

0 голосов
/ 03 января 2019

CollectionViewSource с Фильтр поможет.

Здесь - базовый пример использования фильтра при поиске с использованием CollectionViewSource

...