I'm trying to make a user control with a dependency property. I need to execute certain logic when the dependency property is changed from outside the usercontrol, but that logic shouldn't execute when the dependency property is changed from inside the user control. I have this small sample. I only want to execute certain logic when the value is set from mainwindow and not when it is set by clicking the checkbox. I don't know if PropertyChangedCallbackis the correct way, but this is what I have.
UserControl:
public partial class UserControl1 : UserControl
{
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new PropertyMetadata(new PropertyChangedCallback(OnPropertyChanged)));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Only process the 5, don't process the 6
}
public UserControl1()
{
InitializeComponent();
}
private void checkBox_Click(object sender, RoutedEventArgs e)
{
MyProperty = 6;
}
}
UserControl xaml:
<UserControl x:Class="WpfApplication4.UserControl1"
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="300" d:DesignWidth="300">
<Grid>
<CheckBox x:Name="checkBox" Click="checkBox_Click"/>
</Grid>
</UserControl>
MainWindow:
public partial class MainWindow : Window
{
public int MainWindowProperty { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MainWindowProperty = 5;
}
}
Mainwindow xaml:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 MyProperty="{Binding MainWindowProperty}"/>
</Grid>
</Window>
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!disableProcessing)
{
// Only process the 5, don't process the 6
}
}
bool disableProcessing = false;
private void checkBox_Click(object sender, RoutedEventArgs e)
{
disableProcessing = true;
MyProperty = 6;
disableProcessing = false;
}
Related
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;
}
}
}
}
I created a Dependency Property in the usercontrol, but however changes in the usercontrol was NOT notified to the Viewmodel
Usercontrol
<UserControl x:Class="DPsample.UserControl1"
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="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="txtName"></TextBox>
</Grid>
UserControl.cs
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
#region SampleProperty
public static readonly DependencyProperty SamplePropertyProperty
= DependencyProperty.Register("SampleProperty",
typeof(string),
typeof(UserControl1),
new PropertyMetadata(OnSamplePropertyChanged));
public string SampleProperty
{
get { return (string)GetValue(SamplePropertyProperty); }
set { SetValue(SamplePropertyProperty, value); }
}
static void OnSamplePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
(obj as UserControl1).OnSamplePropertyChanged(e);
}
private void OnSamplePropertyChanged(DependencyPropertyChangedEventArgs e)
{
string SamplePropertyNewValue = (string)e.NewValue;
txtName.Text = SamplePropertyNewValue;
}
#endregion
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPsample" x:Class="DPsample.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 SampleProperty="{Binding SampleText,Mode=TwoWay}" HorizontalAlignment="Left" Margin="76,89,0,0" VerticalAlignment="Top" Width="99"/>
<Button Content="Show" HorizontalAlignment="Left" Margin="76,125,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
MainWindow.cs
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var item = this.DataContext as MainViewModel;
MessageBox.Show(item.SampleText.ToString());
}
MainViewModel.cs
public class MainViewModel : NotifyViewModelBase
{
public MainViewModel()
{
this.SampleText = "test";
}
private string _sampleText;
public string SampleText
{
get { return _sampleText; }
set { _sampleText = value; OnPropertyChanged("SampleText"); }
}
}
Bind the TextBox.Text property in the UserControl to its SampleProperty like this:
<TextBox Text="{Binding SampleProperty,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Now you could simply remove your OnSamplePropertyChanged callback.
You might also register SampleProperty to bind two-way by default like this:
public static readonly DependencyProperty
SamplePropertyProperty = DependencyProperty.Register(
"SampleProperty", typeof(string), typeof(UserControl1),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
An alternative way to do this is an ElementName Binding. First assign the x:Name attribute to the UserControl (for example x:Name="MyUC"), then change the binding to:
<TextBox Text="{Binding ElementName=MyUC, Path=SampleProperty}"/>
I have a window and an usercontrol used within this window.
And I have done the following:
Bound the window property to window's viewmodel property.
Bound the usercontrol property to usercontrol's viewmodel property.
Bound the usercontrol's property to window's viewmodel property.
In my opinion, I think if I set window property by specific value, usercontrol's viewmodel's property will be set to the same value.
But usercontrol's viewmodel's property was not set to the same value.
Here is whole codes.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window1 w = new Window1();
w.Mode = RegisterMode.Update;
w.Show();
}
}
Window1.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:WindowViewModel></local:WindowViewModel>
</Window.DataContext>
<Grid>
<local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100" Mode="{Binding DataContext.Mode, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None));
public Window1()
{
InitializeComponent();
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window1.ModeProperty, modeBinding);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString());
}
}
WindowViewModel.cs
public class WindowViewModel : INotifyPropertyChanged
{
RegisterMode mode = RegisterMode.None;
public event PropertyChangedEventHandler PropertyChanged;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UserControl1.xaml
<UserControl x:Class="MVVMTest2.UserControl1"
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:MVVMTest2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:UCViewModel></local:UCViewModel>
</UserControl.DataContext>
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None));
public UserControl1()
{
InitializeComponent();
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(UserControl1.ModeProperty, modeBinding);
}
}
UCViewModel.cs
public class UCViewModel : INotifyPropertyChanged
{
RegisterMode mode = RegisterMode.None;
public event PropertyChangedEventHandler PropertyChanged;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What point am I doing wrong?
Please let me know about it.
Try this solution using composition between your ViewModels.
Window1.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1"
Title="Window1" Height="300" Width="300"
Mode={Binding UcViewModel.Mode}> <!-- Added this binding -->
<!-- MOVED THIS TO THE CODE-BEHIND
<Window.DataContext>
<local:WindowViewModel></local:WindowViewModel>
</Window.DataContext>
-->
<Grid>
<local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100"
Mode="{Binding UcViewModel.Mode}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None));
public Window1()
{
InitializeComponent();
/* THIS IS NOT NEEDED
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window1.ModeProperty, modeBinding)
*/
// Use the following instead:
this.DataContext = new WindowViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString());
}
}
UserControl1.xaml
<UserControl x:Class="MVVMTest2.UserControl1"
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:MVVMTest2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<!-- THIS PART IS NOT NEEDED
<UserControl.DataContext>
<local:UCViewModel></local:UCViewModel>
</UserControl.DataContext>
-->
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None));
public UserControl1()
{
InitializeComponent();
/* THIS IS NOT NEEDED
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(UserControl1.ModeProperty, modeBinding);
*/
}
}
WindowViewModel.cs
public class WindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/* Remove this:
RegisterMode mode = RegisterMode.None;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
*/
// Use composition instead
UCViewModel _ucViewModel = null;
public UCViewModel UcViewModel
{
get
{
if (_ucViewModel == null)
{
_ucViewModel = new UCViewModel();
}
return _ucViewModel;
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And one more thing, you might want to rename the ViewModel property Mode to another name like RegMode. The reason is that it's confusing in the XAML code during binding.
From this:
{Binding Mode, Mode=TwoWay}
Or, this:
{Binding Path=Mode, Mode=TwoWay}
To this less confusing one:
{Binding RegMode, Mode=TwoWay}
In the code below, I have a UserControl that contains an ellipse and a textblock. I'd like to create a reusable control that I can bind to that allows me to set the text based on a string, and changes the fill color between Red/Green based on a boolean.
I can do this now by digging deep into the markup and using some complex binding, but I want to reuse this control in a list and it seemed easier to create a control for the purpose. However, I am not sure where to go next and if I should be creating dependency-properties that tie to the values for Fill and Text, or what.
<UserControl
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"
x:Class="Herp.Derp.View.DeliveryStatusIndicator"
x:Name="UserControl"
d:DesignWidth="91" d:DesignHeight="35">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<Ellipse Width="35" Height="35" Fill="Green">
<Ellipse.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_location_circle}"/>
</Ellipse.OpacityMask>
</Ellipse>
<TextBlock Style="{StaticResource Heading2}"
VerticalAlignment="Center" Margin="3,0,0,0">
<Run Text="FRONT"/>
</TextBlock>
</StackPanel>
</Grid>
</UserControl>
here how you can achieve this
UserControl
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty FrontTextProperty = DependencyProperty.Register( "FrontText", typeof(string),typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string FrontText
{
get { return (string)GetValue(FrontTextProperty); }
set {
SetValue(FrontTextProperty, value);
frontBlock.Text = value;
}
}
public static readonly DependencyProperty EllipseStateProperty = DependencyProperty.Register("EllipseState", typeof(bool), typeof(UserControl1), new FrameworkPropertyMetadata(false));
public bool EllipseState
{
get { return (bool)GetValue(EllipseStateProperty); }
set
{
SetValue(EllipseStateProperty, value);
if (value)
{
ellipse.Fill = new SolidColorBrush( Colors.Green);
}
else
{
ellipse.Fill = new SolidColorBrush(Colors.Red);
}
}
}
}
MainWindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 EllipseState="{Binding yourProperty }"/>
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="207,94,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
Yes, to create properties that "parent" XAML can assign bindings to, you need to create aDependencyProperty for each field that you want to bind to.
You would then bind your user control xaml to the backing property for the DP.
Here's what I got for a working solution:
public partial class DeliveryStatusIndicator : UserControl
{
public DeliveryStatusIndicator()
{
InitializeComponent();
}
public static readonly DependencyProperty DeliveryDescriptionProperty = DependencyProperty.Register("DeliveryDescription", typeof(string), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata("Default", DescriptionChangedEventHandler));
private static void DescriptionChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Desc.Text = (string)e.NewValue;
}
public string DeliveryDescription
{
get { return (string)GetValue(DeliveryDescriptionProperty); }
set
{
SetValue(DeliveryDescriptionProperty, value);
}
}
public static readonly DependencyProperty DeliveryStatusProperty = DependencyProperty.Register("DeliveryStatus", typeof(bool), typeof(DeliveryStatusIndicator), new FrameworkPropertyMetadata(false, StatusChangedEventHandler));
private static void StatusChangedEventHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DeliveryStatusIndicator)d).Indicator.Fill = (bool)e.NewValue ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
}
public bool DeliveryStatus
{
get { return (bool)GetValue(DeliveryStatusProperty); }
set
{
SetValue(DeliveryStatusProperty, value);
}
}
}
I am trying to implement Help functionality for my wpf application which is following the MVVM pattern. I have my help file present, which contains many pages according to the application. Now I need to integrate this into my application.
Here are my requirements:
Pressing F1 opens a certain page in the help file depending on the view model. For this, I guess, I need to bind the F1 command to my view model. How do we bind keys in views?
Pressing F1 on a text field opens help for that text field. I think it will be the same as requirement 1. But the problem here is how will I know that a certain text field, button, or radio button is selected?
Listen for the key in the view (or a base class of the view) and call execute on a HelpCommand on the DataContext.
Pass the control that has focus (or its id, or tag, ...) as an argument to the HelpCommand.
Alternative way to find the focussed control by using the FocusManager
Here is an example:
ContextHelp C#:
public static class ContextHelp
{
public static readonly DependencyProperty KeywordProperty =
DependencyProperty.RegisterAttached(
"Keyword",
typeof(string),
typeof(ContextHelp));
public static void SetKeyword(UIElement target, string value)
{
target.SetValue(KeywordProperty, value);
}
public static string GetKeyword(UIElement target)
{
return (string)target.GetValue(KeywordProperty);
}
}
ViewBase:
public abstract class ViewBase : UserControl
{
public ViewBase()
{
this.KeyUp += ViewBase_KeyUp;
this.GotFocus += ViewBase_GotFocus;
}
void ViewBase_GotFocus(object sender, RoutedEventArgs e)
{
FocusManager.SetIsFocusScope(this, true);
}
void ViewBase_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.F1)
{
var viewModel = this.DataContext as ViewModelBase;
if (viewModel != null)
{
var helpTopic = "Index";
var focusedElement =
FocusManager.GetFocusedElement(this) as FrameworkElement;
if (focusedElement != null)
{
var keyword = ContextHelp.GetKeyword(focusedElement);
if (!String.IsNullOrWhiteSpace(keyword))
{
helpTopic = keyword;
}
}
viewModel.HelpCommand.Execute(helpTopic);
}
}
}
}
ViewModelBase:
public abstract class ViewModelBase: INotifyPropertyChanged
{
public ICommand HelpCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName="")
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
AViewModel:
class AViewModel : ViewModelBase
{
public AViewModel()
{
HelpCommand = new RelayCommand(HelpCommandExecuted, (p)=>true);
}
private void HelpCommandExecuted(object parameter)
{
var topic = parameter as string;
if (!String.IsNullOrWhiteSpace(topic))
{
HelpText = String.Format("Information on the interesting topic: {0}.", topic);
}
}
private string _helpText;
public string HelpText
{
get { return _helpText; }
private set
{
if (_helpText != value)
{
_helpText = value;
OnPropertyChanged();
}
}
}
}
AView C#:
public partial class AView : ViewBase
{
public AView()
{
InitializeComponent();
}
}
AView XAML:
<local:ViewBase x:Class="WpfApplication2.AView"
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:WpfApplication2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="{Binding HelpText}" Margin="10,254,10,0" VerticalAlignment="Top" Height="36"/>
<Button local:ContextHelp.Keyword="Button Info" Content="Button" HorizontalAlignment="Left" Margin="192,32,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox local:ContextHelp.Keyword="TextBox Info" HorizontalAlignment="Left" Height="23" Margin="29,32,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<CheckBox local:ContextHelp.Keyword="CheckBox Info" Content="CheckBox" HorizontalAlignment="Left" Margin="29,80,0,0" VerticalAlignment="Top"/>
<ComboBox local:ContextHelp.Keyword="ComboBox Info" HorizontalAlignment="Left" Margin="138,80,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
</local:ViewBase>
MainWindow XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2" x:Class="WpfApplication2.MainWindow"
Title="MainWindow" Height="700" Width="500">
<Grid x:Name="ViewPlaceholder">
</Grid>
</Window>
MainWindow C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var view = new AView();
var viewModel = new AViewModel();
view.DataContext = viewModel;
ViewPlaceholder.Children.Clear();
ViewPlaceholder.Children.Add(view);
}
}