Sabyasachi Mukherjee Ответов: 1

ViewModel для пользовательского элемента управления?


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

Вот XAML для дизайна:

<UserControl x:Class="MyDiskTools.UserControls.NodeGrid.NodeGrid"

             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:MyDiskTools.UserControls.NodeGrid"

             mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="Padding" Value="{Binding ButtonPadding, FallbackValue=5}"/>
                <Setter Property="BorderThickness" Value="{Binding ButtonBorderThickness, FallbackValue=1}"/>
                <Setter Property="Command" Value="{Binding InputCharacterCommand}"/>
                <Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BorderThickness" Value="{Binding HighlightBorderThickness, FallbackValue=5}"/>
                        <Setter Property="FontSize" Value="{Binding HighlightFontSize, FallbackValue=20}"/>                        
                    </Trigger>
                </Style.Triggers>
            </Style>            
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <UniformGrid Grid.Row="0" Rows="1">
            <Button Content="A" />
            <Button Content="B" />
            <Button Content="C" />
            <Button Content="D" />
            <Button Content="E" />
            <Button Content="F" />
        </UniformGrid>
        <UniformGrid Grid.Row="1" Rows="1">
            <Button Content="G" />
            <Button Content="H" />
            <Button Content="I" />
            <Button Content="J" />
            <Button Content="K" />
            <Button Content="L" />
            <Button Content="M" />
        </UniformGrid>
        <UniformGrid Grid.Row="2" Rows="1">
            <Button Content="N" />
            <Button Content="O" />
            <Button Content="P" />
            <Button Content="Q" />
            <Button Content="R" />
            <Button Content="S" />
            <Button Content="T" />
        </UniformGrid>
        <UniformGrid Grid.Row="3" Rows="1">
            <Button Content="U" />
            <Button Content="V" />
            <Button Content="W" />
            <Button Content="X" />
            <Button Content="Y" />
            <Button Content="Z" />
        </UniformGrid>
        <TextBox Name="InputMessage"  IsEnabled="False" Background="Beige" Grid.Row="4" Text="{Binding PasswordDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</UserControl>


Код-за-Здесь:

public partial class NodeGrid : UserControl
    {
        public NodeGrid()
        {
            InitializeComponent();
            DataContext = new NodeGridVM();
        }

        public string Message
        {
            get
            {
                return InputMessage.Text;
            }
        }

        #region Dependency Properties
        [TypeConverter(typeof(LengthConverter))]

        
        public double InternalPadding
        {
            get { return (double)GetValue(InternalPaddingProperty); }
            set { SetValue(InternalPaddingProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InternalPadding.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InternalPaddingProperty =
            DependencyProperty.Register("InternalPadding", typeof(double), typeof(NodeGrid), new PropertyMetadata(double.NaN));

        public double ButtonHeight
        {
            get { return (double)GetValue(ButtonHeightProperty); }
            set { SetValue(ButtonHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ButtonHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ButtonHeightProperty =
            DependencyProperty.Register("ButtonHeight", typeof(double), typeof(NodeGrid), new PropertyMetadata(double.NaN));
        #endregion
    }


... а ViewModel-это:

class NodeGridVM : INotifyPropertyChanged
    {
        private string _passwordDisplay = "";

        public string PasswordDisplay
        {
            get
            {
                return _passwordDisplay;
            }

            set
            {
                if (value != _passwordDisplay)
                {
                    _passwordDisplay = value;
                    OnPropertyChange();
                }
            }
        }

        private ICommand _inputCharacterCommand;

        public ICommand InputCharacterCommand
        {
            get
            {
                if (_inputCharacterCommand == null)
                {
                    InputCharacterCommand = new InputCharacterCommand(this);
                }
                    
                return _inputCharacterCommand;
            }

            set
            {
                _inputCharacterCommand = value;
            }
        }
        public void AddCharacter(string input)
        {
            if (input != null)
            {
                PasswordDisplay = String.Concat(PasswordDisplay, input);
            }            
        }

        private void OnPropertyChange([CallerMemberName] string property = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public bool InputAllowed()
        {
            if (PasswordDisplay == null)
            {
                return true;
            }

            if (PasswordDisplay.Length > 20)
            {
                return false;
            }

            return true;
        }
    }

    class InputCharacterCommand : ICommand
    {
        private NodeGridVM _viewmodel;

        public InputCharacterCommand(NodeGridVM vm)
        {
            _viewmodel = vm;
        }

        public event EventHandler CanExecuteChanged;
        

        public bool CanExecute(object parameter)
        {
            return (_viewmodel.InputAllowed());
        }

        public void Execute(object parameter)
        {
            _viewmodel.AddCharacter(parameter as String);
        }
    }


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

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

Однако, согласно большинству мнений в интернете, предоставление Usercontrol собственной модели представления-крайне плохая идея. Должен ли я тогда отказаться от ViewModel и просто поместить всю логику в файл code-behind?

[no name]

"согласно большинству мнений в интернете, предоставление Usercontrol собственной модели представления-это крайне плохая идея".... согласно каким источникам?

Sabyasachi Mukherjee

Источник 1:
http://stackoverflow.com/questions/1939345/wpf-should-a-user-control-have-its-own-viewmodel

[no name]

Итак, один человек говорит, что это плохая идея, и вы интерпретируете это как Евангелие? Я не думаю, что это плохая идея. Это зависит от приложения.

Sabyasachi Mukherjee

Вообще говоря, это хорошая практика, чтобы дать Usercontrol свою собственную виртуальную машину? Или это всецело зависит от конкретных случаев?

[no name]

Опять же, это зависит.

Sabyasachi Mukherjee

Источник 2:
http://stackoverflow.com/questions/28175338/hair-loss-and-mvvm-user-controls

Ludovic Feltz

Это зависит от того, что вы действительно хотите сделать... Источник 1 был обновлен с ответом, который объясняет, почему вы можете...

1 Ответов

Рейтинг:
2

Graeme_Grant

Цитата:
согласно большинству мнений в интернете, предоставление Usercontrol собственной модели представления-крайне плохая идея
Это что-то новое для меня. Народное мнение на самом деле совершенно противоположно! Для меня это личное предпочтение или специфика использования.

Например, у меня может быть сложная форма ввода с большим количеством Xaml. Форма имеет виртуальную машину (ViewModel), Но области формы разбиты на отдельные UCs (UserControls).

Другим примером может быть то, что каждый регион в форме имеет определенную задачу, поэтому форма-это просто контейнер без виртуальной машины, и каждый регион имеет свою собственную функцию, поэтому существует несколько ПСК, каждый со своими собственными виртуальными машинами.

Самое простое правило фреймворка, которое нужно запомнить,-это то, что код пользовательского интерфейса принадлежит коду позади, логика в виртуальной машине.

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

Надеюсь, это поможет!


Sabyasachi Mukherjee

Спасибо, что прояснили этот вопрос. Я был доволен своим UserControl, пока сегодня не прочитал мнения о собственных виртуальных машинах UserControl.

Graeme_Grant

Есть те, кто думает, что знает, о чем говорит, и есть те, кто знает, о чем говорит... ;)

[no name]

И те, кто думает, что они знают, о чем говорят, на самом деле п$СС от тех из нас, кто действительно знает. :-)

Ludovic Feltz

И те, кто не знает, о чем они не говорят, и пытаются учиться у тех, кто думает, что они знают, и у тех, кто на самом деле знает (но им трудно понять, кто из них действительно знает...) ;-) и, наконец, узнать, что оба решения применимы, зависит от того, что вы хотите сделать....