I have a WPF app where I am trying to create an on-screen keyboard as a Usercontrol.
Here is the XAML for the design:
<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>
The code-behind is here:
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); }
}
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); }
}
public static readonly DependencyProperty ButtonHeightProperty =
DependencyProperty.Register("ButtonHeight", typeof(double), typeof(NodeGrid), new PropertyMetadata(double.NaN));
#endregion
}
... and the ViewModel is:
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);
}
}
Please do note that this is just a sketch-up of the code, and more dependency properties will be created for future use.
What I have tried:
However, according to most of the opinions online, giving a Usercontrol a viewmodel of its own is an extremely bad idea. Should I, then, scrap the ViewModel and just put all the logic in the code-behind file?