Rahul96553 Ответов: 2

Динамическая привязка Wpf MVVM usercontrols и show records в соответствии с выбранным дизайном


Мне нужна помощь в моем проекте.Не смог получить ни одного правильного решения, как я хочу. Я объясню вам свой сценарий здесь.

Я работаю над проектом WPF с шаблоном MVVM, где у меня есть главный экран Windows, при загрузке главного экрана Windows он должен быть пустым, и там есть только одна метка. При нажатии на эту метку я должен открыть новую форму, которая является экраном виджета, а экран виджета имеет несколько кнопок(в количестве 12) с изображениями.

Теперь, при нажатии любой кнопки, я хочу, чтобы это конкретное имя кнопки или параметр, поэтому соответственно этому имени или параметру я должен найти пользовательские элементы управления из представлений и визуализировать пользовательские элементы управления на главном экране Windows с коллекцией из 20 записей. Если я снова нажму на ярлык главного окна и выберу другую кнопку с экрана виджета, то он должен загрузить еще один выбранный пользовательский элемент управления, который будет привязан к главному экрану Windows.

Итак, короче говоря, в соответствии с выбранным дизайном с экрана виджета все записи должны быть показаны в основных окнах с помощью пользовательских элементов управления.
Это мой основной код экрана Windows, в настоящее время он связывает статический пользовательский элемент управления, который я упомянул непосредственно в элементах управления из этих 12 пользовательских элементов управления, один из которых-UserControlColumn1XL, как он должен быть динамически изменен со всем сценарием?

Что я уже пробовал:

<Window x:Class="TechMedic.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:uc ="clr-namespace:TechMedic.View.UserControls"
    xmlns:local="clr-namespace:TechMedic"
    xmlns:controls="clr-namespace:TechMedic.View.UserControls"
    xmlns:data="clr-namespace:TechMedic.Data"
    mc:Ignorable="d"
    WindowStartupLocation="CenterScreen" Background="Black"
    Title="Main Window}" WindowState="Maximized" MinWidth="750" >

<StackPanel>    
    <StackPanel Grid.Column="0" Background="Black" VerticalAlignment="Top" >
        <Border BorderThickness="1" Background="Black" BorderBrush="White">
            <Button Style="{StaticResource TransparentStyle}" Command="{Binding OnClickNormalCommand}" CommandParameter="Normal" Foreground="LightSkyBlue" HorizontalAlignment="Center" FontSize="12pt" >
                <TextBlock Text="{Binding NormalCollection.Count, StringFormat='Normal: {0}'}" Padding="0,5, 0, 5" Foreground="{Binding IsNormalColor}" />
            </Button>
            </Border>
            <Border Name="UserControl1BorderNormal" BorderBrush="White" BorderThickness="0" >
                <ItemsControl ItemsSource="{Binding NormalCollection}" Name="ListNormal" Margin="4,0" >
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel HorizontalAlignment="Left" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border>
        </StackPanel>
    </StackPanel>
</Window>

Dirk Bahle

Не знаю, в чем заключается ваш вопрос - вы показываете только элементы управления, но не модель или viewmodel. Чтобы трудно было читать твои мысли :-(

Rahul96553

Это слишком длинный код, чтобы помещать его здесь. так что объясню вам проще всего.

Actually ,The Project includes a MainWindow which has a Label. I want to open a Widget form with Multiple Image Button on Label's Click command. Each Image button will return List of Parameters on MainWindowViewModel. The list will bind with UserControl on MainWindow Screen. If I repeat the Same thing by choosing different Image Button. The MainWindowViewModel will Replace with new List. But in this case I want change UserControl for Example in this line : <controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" /> . Here, UserControlColumn1XL is Static for now. But I want to change it dynamically on Selected Widget Button. I am not getting how to made it possible. So I am waiting for your valuable Suggestions.

Nathan Minier

Две вещи:
Является выбранным элементом управления, представленным свойством в ViewModel.

Является ли класс, представляющий выбранный элемент управления полиморфным, или он изменяется только в зависимости от конкретных свойств?

2 Ответов

Рейтинг:
7

Gerry Schmitz

Ваш "вид" похоронен в шаблоне данных элемента управления items, застрявшего в панели стека с кучей элементов управления second-fiddle.

Вам нужно переосмыслить свою "архитектуру представления", чтобы она была более "управляемой" с точки зрения динамической загрузки.


Рейтинг:
15

Graeme_Grant

Есть несколько вопросов выше. Чтобы все было просто, я попытаюсь ответить на часть о навигации в главном окне с помощью Шаблон проектирования MVVM.

Я уверен, что есть много людей, у которых есть разные идеи о том, что является лучшим решением, и есть много библиотек, которые реализуют различные решения, основанные на Шаблон проектирования MVVM использование инъекции зависимостей (DI) и инверсии контроля (IOC).

Для решений, использующих навигацию, WPF имеет Рамка[^] контроль и управление Страница[^] вид.

Если вы ищете что-то более легкое, вы можете использовать объект ContentPresenter[^ управление для размещения пользовательских элементов управления видом. Это решение, которое я использую ниже.

Потому что мы используем Шаблон проектирования MVVM, мы не можем напрямую ссылаться на элементы управления из модель представления Итак, мы собираемся использовать модель представления навигация и полагаться на WPF привязка данных и Неявные Шаблоны делаю загрузить элемент управления UserControl Просмотры.

Во - первых, нам нужно добавить некоторые основные механизмы привязки данных к свойствам и командования событиями кнопок. Они являются ядром для Шаблон проектирования MVVM.

1. INotiftyPropertyChanged

public abstract class ObservableBase : INotifyPropertyChanged
{
    public void Set<TValue>(ref TValue field, 
                            TValue newValue, 
                            [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<TValue>.Default.Equals(field, default(TValue))
            || !field.Equals(newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

2. ViewModelBase
public abstract class ViewModelBase : ObservableBase
{
    public bool IsInDesignMode()
    {
        return Application.Current.MainWindow == null
            ? true
            : DesignerProperties.GetIsInDesignMode(Application.Current.MainWindow);
    }
}

3. ICommand
public class RelayCommand<T> : ICommand
{
    private readonly Action<T> execute;
    private readonly Predicate<T> canExecute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        this.execute = execute ?? throw new ArgumentNullException("execute");
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
        => canExecute == null || canExecute((T)parameter);

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public void Execute(object parameter)
        => execute(parameter == null
            ? default(T)
            : (T)Convert.ChangeType(parameter, typeof(T)));
}

Далее, нам нужно настроить наш страницы Есть две части:: модель представления и Смотреть (элемент управления UserControl). Это всегда хорошая форма, чтобы держать модель представления и Смотреть с той же приставкой.

4. [Page x]ViewModels
public interface IPage
{
    string Title { get; }
}

public abstract class PqgeViewModelBase : ViewModelBase, IPage
{
    public PqgeViewModelBase(string title)
    {
        Title = title;
        if (!IsInDesignMode())
        {
            // runtime only code here...
            Title += " is alive!";
        }
    }

    public string Title { get; }
}

public class Page1ViewModel : PqgeViewModelBase
{
    // Add page specific code here....
    public Page1ViewModel() : base(nameof(Page1ViewModel))
    {
    }
}

public class Page2ViewModel : PqgeViewModelBase
{
    // Add page specific code here....
    public Page2ViewModel() : base(nameof(Page2ViewModel))
    {
    }
}

5. [Page x]Views
<UserControl x:Class="WpfCPNav.Page1View"

             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" 

             mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
    <Viewbox>
        <TextBlock Text="{Binding Title}"/>
    </Viewbox>
</UserControl>

<UserControl x:Class="WpfCPNav.Page2View"

             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" 

             mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
    <Viewbox>
        <TextBlock Text="{Binding Title}"/>
    </Viewbox>
</UserControl>

Теперь, когда у нас есть страницы, мы готовы разместить их в интернете. MainWindow из приложения. То MainViewModel будет обрабатывать переключение видов.

6. MainViewModel
public class MainViewModel : ViewModelBase
{
    private IPage content;
    public IPage Content { get => content; private set => Set(ref content, value); }

    public RelayCommand<int> NavigateCommand => new RelayCommand<int>(Navigate);

    private readonly Dictionary<int, Lazy<IPage>> pages
        = new Dictionary<int, Lazy<IPage>>
    {
        [1] = new Lazy<IPage>(() => new Page1ViewModel()),
        [2] = new Lazy<IPage>(() => new Page2ViewModel())
    };

    public MainViewModel() => Navigate(1);
    private void Navigate(int value) => Content = pages[value].Value;
}

Наконец, нам нужно сделать пользовательский интерфейс. Поскольку мы используем неявные шаблоны, мы определяем их в MainWindow.

7. MainWindow
<Window x:Class="WpfCPNav.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:v="clr-namespace:WpfCPNav"

        xmlns:vm="clr-namespace:WpfCPNav"



        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d" WindowStartupLocation="CenterScreen"

        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:Page1ViewModel}">
            <v:Page1View />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:Page2ViewModel}">
            <v:Page2View />
        </DataTemplate>

        <Style TargetType="Button">
            <Setter Property="Margin" Value="10"/>
            <Setter Property="Padding" Value="10 5"/>
        </Style>

        <Style TargetType="StackPanel">
            <Setter Property="Orientation" Value="Horizontal"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>

    </Window.Resources>


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

        <StackPanel>
            <Button Content="PAGE 1" Command="{Binding NavigateCommand}" CommandParameter="1" />
            <Button Content="PAGE 2" Command="{Binding NavigateCommand}" CommandParameter="2" />
        </StackPanel>

        <ContentPresenter Content="{Binding Content}" Grid.Row="1"/>
    </Grid>

</Window>

Как это работает:
Когда Button щелкает, то MainViewModel пройдет выбранное MainViewModel к ContentPresenter контроль, то ContentPresenter контроль будет их искать шаблон и загрузит его работы с произвольными связанный элемент управления UserControl и модель представления is is automatically автоматически устанавливается в UserControl DataContext собственность и привязка данных примененный.