WPF MVVM Key Binding - c#

I'm new to WPF, and the code I've written doesn't seem to be working. I'm trying to move a rectangular paddle to the left when the left arrow key is pressed, but I get no response when I press the Left arrow key. What am I doing wrong here?
Here is the View:
<UserControl.InputBindings>
<KeyBinding Key="Left" Command="{Binding MovePaddleLeftCommand}"/>
</UserControl.InputBindings>
and here is the code in the ViewModel:
private ICommand _movePaddleLeftCommand;
public ICommand MovePaddleLeftCommand
{
get
{
return new DelegateCommand(ExecuteCommand, CanExecute);
}
}
private void ExecuteCommand()
{
MessageBox.Show("Left");
MovePaddleLeft();
}
private bool CanExecute()
{
return true;
}
Below is a more complete picture of the code in case it's relevant.
GameView.xaml:
<UserControl x:Class="Arkanoid_MkII.Views.GameView"
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:Arkanoid_MkII.Views"
mc:Ignorable="d"
Width="1500" Height="800">
<Canvas Name="GameArea" ClipToBounds="True">
<Rectangle Canvas.Left="{Binding Path=Paddle.PaddleX}" Canvas.Top="750" Width="{Binding Path=Paddle.Width}" Height="{Binding Path=Paddle.Height}" Fill="{Binding Path=Paddle.Colour}"/>
</Canvas>
<UserControl.InputBindings>
<KeyBinding Key="Left" Command="{Binding MovePaddleLeftCommand}"/>
</UserControl.InputBindings>
</UserControl>
GameView.xaml.cs:
public partial class GameView : UserControl
{
public GameView()
{
InitializeComponent();
}
}
MainWindow.xaml:
<Window x:Class="Arkanoid_MkII.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:local="clr-namespace:Arkanoid_MkII"
xmlns:views="clr-namespace:Arkanoid_MkII.Views"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight" ResizeMode="NoResize" Background="Black" Padding="100">
<Grid>
<Border BorderBrush="Black" BorderThickness="5">
<views:GameView x:Name="GameViewControl" Loaded="GameViewControl_Loaded" />
</Border>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void GameViewControl_Loaded(object sender, RoutedEventArgs e)
{
Arkanoid_MkII.ViewModels.GameViewModel gameViewModelObject = new Arkanoid_MkII.ViewModels.GameViewModel();
GameViewControl.DataContext = gameViewModelObject;
}
}
GameViewModel.cs:
private ICommand _movePaddleLeftCommand;
public ICommand MovePaddleLeftCommand
{
get
{
return new DelegateCommand(ExecuteCommand, CanExecute);
}
}
private void ExecuteCommand()
{
MessageBox.Show("Left");
MovePaddleLeft();
}
private bool CanExecute()
{
return true;
}

The UserControl must be focusable and focused for the command to be executed:
private void GameViewControl_Loaded(object sender, RoutedEventArgs e)
{
Arkanoid_MkII.ViewModels.GameViewModel gameViewModelObject =
new Arkanoid_MkII.ViewModels.GameViewModel();
GameViewControl.DataContext = gameViewModelObject;
GameViewControl.Focusable = true;
Keyboard.Focus(GameViewControl);
}

Related

Can a single component of a page be full screen in winui3?

This is a video stream playing module implemented by canvas. How to make the following CanvasControl display in full screen in winui 3?
<Grid>
<canvas:CanvasControl x:Name="canvas"></canvas:CanvasControl>
</Grid>
You create a full-screen window this way. You'll need to add your CanvasControl to both of them.
FullScreenWindow.xaml
<Window
x:Class="FullScreen.FullScreenWindow"
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:local="using:FullScreen"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<Button
Click="CloseButton_Click"
Content="Close" />
</StackPanel>
</Window>
FullScreenWindow.xaml.cs
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System;
namespace FullScreen;
public sealed partial class FullScreenWindow : Window
{
public FullScreenWindow()
{
this.InitializeComponent();
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
ThisAppWindow = AppWindow.GetFromWindowId(windowId);
ThisAppWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
}
public static AppWindow? ThisAppWindow;
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
MainWindow.xaml
<Window
x:Class="FullScreen.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">
<Grid>
<Button
Click="Button_Click"
Content="Launch full-screen window" />
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
namespace FullScreen;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
private FullScreenWindow? fullScreenWindow;
private void Button_Click(object sender, RoutedEventArgs e)
{
fullScreenWindow = new();
fullScreenWindow.Activate();
}
}
BTW, we have MediaPlayer for playing videos.
UPDATE
This way you can pass a control to the other Window. This works with a TextBox so I guess it should work with your CanvasControl too.
FullScreenWindow.xaml
<Window
x:Class="FullScreen.FullScreenWindow"
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:local="using:FullScreen"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<Button
Click="CloseButton_Click"
Content="Close" />
<Grid x:Name="InputBoxControlGrid" />
</StackPanel>
</Window>
FullScreenWindow.xaml.cs
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System;
namespace FullScreen;
public sealed partial class FullScreenWindow : Window
{
public static AppWindow? ThisAppWindow;
public FullScreenWindow()
{
this.InitializeComponent();
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
ThisAppWindow = AppWindow.GetFromWindowId(windowId);
ThisAppWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
}
private UIElement? InputBoxControl { get; set; }
public void InstallInputBoxControl(UIElement inputBoxControl)
{
InputBoxControl = inputBoxControl;
this.InputBoxControlGrid.Children.Add(InputBoxControl);
}
public UIElement? UninstallInputBoxControl()
{
this.InputBoxControlGrid.Children.Remove(InputBoxControl);
UIElement? inputBoxControl = this.InputBoxControl;
this.InputBoxControl = null;
return inputBoxControl;
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
MainWindow.xaml
<Window
x:Class="FullScreen.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:local="using:FullScreen"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<Button
Click="Button_Click"
Content="Launch full-screen window" />
<ContentControl x:Name="InputBoxContainer">
<TextBox />
</ContentControl>
</StackPanel>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
namespace FullScreen;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
private FullScreenWindow? FullScreenWindow { get; set; }
public void InstallInputBoxControl(UIElement inputBoxControl)
{
this.InputBoxContainer.Content = inputBoxControl;
}
public UIElement? UninstallInputBoxControl()
{
UIElement? inputBoxControl = this.InputBoxContainer.Content as UIElement;
this.InputBoxContainer.Content = null;
return inputBoxControl;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
FullScreenWindow = new();
if (UninstallInputBoxControl() is UIElement content)
{
FullScreenWindow.InstallInputBoxControl(content);
}
FullScreenWindow.Closed += FullScreenWindow_Closed;
FullScreenWindow.Activate();
}
private void FullScreenWindow_Closed(object sender, WindowEventArgs args)
{
if (sender is FullScreenWindow fullScreenWindow)
{
if (fullScreenWindow.UninstallInputBoxControl() is UIElement inputBoxControl)
{
this.InputBoxContainer.Content = inputBoxControl;
}
}
}
}

WPF ItemsControl can't bind Command

I'm a newbie in wpf and i know that this question has been asked other times, and i tried to implement some solutions that i found. But it's not working. I'm doing something wrong but i can't see what it is.
I've created a new simple application to test this problem.
namespace WpfApp3
{
public class MyElement
{
public string Text { get; set; }
public MyElement(string t)
{
Text = t;
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler(parameter);
}
private Action<object> _handler;
public MyCommand(Action<object> handler) { _handler = handler; }
}
public class MyItemsControlViewModel
{
ObservableCollection<MyElement> _items;
public ObservableCollection<MyElement> MyElementItems { get { return _items; } set { _items = value; RaisePropertyChanged("MyElementItems"); } }
ObservableCollection<MyElement> _temporayList;
private ICommand _itemClicked;
public ICommand ItemClicked { get { return _itemClicked; } }
public MyItemsControlViewModel()
{
_items = new ObservableCollection<MyElement>();
_temporayList = new ObservableCollection<MyElement>();
_itemClicked = new MyCommand(OnItemSelected);
AddItem("Element 1");
AddItem("Element 2");
AddItem("Element 3");
UpdateList();
}
public void UpdateList()
{
MyElementItems = _temporayList;
}
public void AddItem(string t)
{
MyElement item = new MyElement(t);
_temporayList.Add(item);
}
public void OnItemSelected(object param)
{
Debug.WriteLine("Executed!");
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<UserControl x:Class="WpfApp3.MyUserControl"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="1080" d:DesignWidth="570"
x:Name="myCustomControl">
<Grid >
<Button x:Name="btnOutsideItemsControl" Width="100" Height="100 " VerticalAlignment="Top" Command="{Binding ItemClicked}" />
<ItemsControl
x:Name="listItems"
ScrollViewer.PanningMode="None"
IsEnabled="False"
Background = "Transparent"
HorizontalAlignment="Center"
ItemsSource="{Binding MyElementItems}" Margin="0,152,0,0" Width="549">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" Margin="50,0,0,0"
Background="Transparent" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Text}"
Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The component is used in MainWindow.xaml.
namespace WpfApp3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyItemsControlViewModel _myViewModel;
public MyItemsControlViewModel MyViewModel { get { return _myViewModel; } }
public MainWindow()
{
_myViewModel = new MyItemsControlViewModel();
InitializeComponent();
myCustomControl.DataContext = MyViewModel;
}
}
}
XAML
<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyUserControl x:Name="myCustomControl"/>
</Grid>
</Window>
When i run the application i can see correctly the list of 3 items with the correct text.
But if i click on one of the button of the list i can't see the output of Debug.WriteLine("Executed!");
But if i click on the button btnOutsideItemsControl that is outside the ItemsControl, it works. I can see the output of Debug.WriteLine("Executed!");
So i think that also the definition of the command is correct.
To bind correctly the Command property of Button inside the ItemsControl i try this
<Button Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}">
And also this
<Button Command="{Binding DataContext.ItemClicked, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}">
But it not works.
Please help!
You're gonna kick yourself once I tell you.
Your problem is that you set IsEnabled="False" on your ItemsControl. Remove it and all will be well with the universe.

It seems like basic question......Why OnpropertyChanged doesn't work?

Hi I am new for learning WPF MVVM ,please help a basic question ,thx.
I use a for-loop to create a counter ,but View doesn't get the property changed.
I know I should use INotifyPropertyChanged to make view refresh ,but it still cannot work...
Why Codes below doesn't work?
View:
<Window x:Class="WpfApp5.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:local="clr-namespace:WpfApp5"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:DemoVM/>
</Window.DataContext>
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="321,297,0,0" VerticalAlignment="Top" Height="38" Width="115" Click="Button_Click"/>
<ProgressBar HorizontalAlignment="Left" Height="30" Margin="98,217,0,0" VerticalAlignment="Top" Width="560" Value="{Binding Number, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}" Margin="696,217,26,119" FontSize="20"/>
</Grid>
</Window>
ViewModel:
class DemoVM:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
internal void AddNumber()
{
for (int i = 0; i < 100; i++)
{
Number++;
}
}
int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged(nameof(Number));
}
}
}
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DemoVM demoVM = new DemoVM();
demoVM.AddNumber();
}
}
}
ViewDesign:
View
It looks like you already have set the binding context of the view to the DemoVM view model, so in your button click, you need not instantiate a new one.
private void Button_Click(object sender, RoutedEventArgs e)
{
((DemoVM)DataContext).AddNumber();
}

How can I write something in a TextBox that's also used as a KeyBinding?

I have a global input binding for the key period (.). I'd still like to be able to enter it in a TextBox? Is there a way to accomplish this?
Here's a simple sample case. The command is executed when I type the period in the TextBox.
XAML:
<Window x:Class="UnrelatedTests.Case6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.InputBindings>
<KeyBinding Key="OemPeriod" Command="{Binding Command}" />
</Window.InputBindings>
<Grid>
<TextBox >Unable to type "." here!</TextBox>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Input;
namespace UnrelatedTests.Case6
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
public ICommand Command
{
get { return new CommandImpl(); }
}
private class CommandImpl : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
MessageBox.Show("executed!");
}
public event EventHandler CanExecuteChanged;
}
}
}
You can bind the Key in your KeyBinding and change it's value to Key.None when your TextBox got focus:
Xaml:
<Window x:Class="UnrelatedTests.Case6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
GotFocus="Window_GotFocus">
<Window.InputBindings>
<KeyBinding Key="{Binding MyKey}" Command="{Binding Command}" />
</Window.InputBindings>
<Grid>
<TextBox/>
</Grid>
</Window>
MainWindow.cs: (with INotifyPropertyChanged implemented)
Key _myKey;
public Key MyKey
{
get
{
return _myKey;
}
set
{
_myKey = value;
OnPropertyChanged("MyKey");
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
MyKey = Key.OemPeriod;
}
private void Window_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is TextBox)
MyKey = Key.None;
else
MyKey = Key.OemPeriod;
}

How to get mouse wheel to change the background image

I am creating a UserControl and one behaviour I want it to have is that when the user rotates the mouse wheel over it then the background image alternates between two options.
What I have so far is:
<UserControl x:Class="OI.MR.UserControls.DataControls.ScrollWheel"
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="400" d:DesignWidth="118">
<UserControl.Background>
<ImageBrush ImageSource="dial1.png" TileMode="None" />
</UserControl.Background>
<UserControl.InputBindings>
<MouseBinding MouseAction="WheelClick" Command="{Binding ScrollTheWheel}"/>
</UserControl.InputBindings>
</UserControl>
and
public partial class ScrollWheel : UserControl
{
private bool _isDial1 = true;
public ScrollWheel()
{
InitializeComponent();
}
private ICommand _scrollTheWheel;
public ICommand ScrollTheWheel
{
get
{
if(_scrollTheWheel == null)
{
_scrollTheWheel = new DelegateCommand(_ => SwitchImage(), _ => true);
}
return _scrollTheWheel;
}
}
private void SwitchImage()
{
if(_isDial1)
{
(Background as ImageBrush).ImageSource = new BitmapImage(new Uri("dial2.png"));
_isDial1 = false;
}
else
{
(Background as ImageBrush).ImageSource = new BitmapImage(new Uri("dial1.png"));
_isDial1 = true;
}
}
}
However turning the wheel is not changing the background image. How can I get the image to change?
your datacontext is really not correct:
One possibility:
<UserControl.InputBindings>
<MouseBinding MouseAction="WheelClick"
Command="{Binding Path=ScrollTheWheel, RelativeSource={RelativeSource AncestorType={x:Type view:YourUserControl}}}"/>
</UserControl.InputBindings>
(replace the type with the type of your user control)

Categories

Resources