WPF Lookless Control Events - c#

I have the following class:
public class LooklessControl : Control
{
public List<int> IntList { get; private set; }
public int CurrentInt { get; private set; }
private int _index = 0;
static LooklessControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LooklessControl), new FrameworkPropertyMetadata(typeof(LooklessControl)));
}
public LooklessControl()
{
IntList = new List<int>();
for (int i = 0; i < 10; i++)
{
IntList.Add(i);
}
CurrentInt = IntList[_index];
}
public static readonly RoutedCommand NextItemCommand =
new RoutedCommand("NextItemCommand", typeof(LooklessControl));
private void ExecutedNextItemCommand(object sender, ExecutedRoutedEventArgs e)
{
NextItemHandler();
}
private void CanExecuteNextItemCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static readonly RoutedCommand PrevItemCommand =
new RoutedCommand("PrevItemCommand", typeof(LooklessControl));
private void ExecutedPrevItemCommand(ExecutedRoutedEventArgs e)
{
PrevItemHandler();
}
private void CanExecutePrevItemCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public static readonly RoutedEvent NextItemEvent =
EventManager.RegisterRoutedEvent("NextItemEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(LooklessControl));
public event RoutedEventHandler NextItem
{
add { AddHandler(NextItemEvent, value); }
remove { RemoveHandler(NextItemEvent, value); }
}
private void RaiseNextItemEvent()
{
RoutedEventArgs args = new RoutedEventArgs(LooklessControl.NextItemEvent);
RaiseEvent(args);
}
public static readonly RoutedEvent PrevItemEvent =
EventManager.RegisterRoutedEvent("PrevItemEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(LooklessControl));
public event RoutedEventHandler PrevItem
{
add { AddHandler(PrevItemEvent, value); }
remove { RemoveHandler(PrevItemEvent, value); }
}
private void RaisePrevItemEvent()
{
RoutedEventArgs args = new RoutedEventArgs(LooklessControl.PrevItemEvent);
RaiseEvent(args);
}
private void NextItemHandler()
{
_index++;
if (_index == IntList.Count)
{
_index = 0;
}
CurrentInt = IntList[_index];
RaiseNextItemEvent();
}
private void PrevItemHandler()
{
_index--;
if (_index == 0)
{
_index = IntList.Count - 1;
}
CurrentInt = IntList[_index];
RaisePrevItemEvent();
}
}
The class has a default style, in Generic.xaml, that looks like this:
<Style x:Key="{x:Type local:LooklessControl}" TargetType="{x:Type local:LooklessControl}">
<Setter Property="Height" Value="200"/>
<Setter Property="Width" Value="90"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:LooklessControl}">
<Border BorderBrush="Black" BorderThickness="1" Padding="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="LightGray"/>
<Rectangle Grid.Row="1" Fill="Gainsboro"/>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Path Grid.Column="0" x:Name="pathLeftArrow" Data="M0,0.5 L1,1 1,0Z" Width="6" Height="14" Stretch="Fill"
HorizontalAlignment="Center" Fill="SlateBlue"/>
<TextBlock Grid.Column="1" Name="textBlock"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentInt}"
HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Junction" FontSize="13"/>
<Path Grid.Column="2" x:Name="pathRightArrow" Data="M0,0 L1,0.5 0,1Z" Width="6" Height="14" Stretch="Fill"
HorizontalAlignment="Center" Fill="SlateBlue"/>
</Grid>
<ListBox Grid.Row="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="Transparent"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IntList}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How do I make it so that when the user clicks on pathLeftArrow it fires LooklessControl.PrevItemCommand, or or they click on pathRightArrow and it fires LooklessControl.NextItemCommand, or they click on an item in the ListBox and LooklessControl is notified of the newly selected item?
In other words, without adding x:Class to the top of Generic.xaml and thus creating a code-behind file for it, which I assume you wouldn't want to do, how do you handle events for elements in your xaml that don't have a Command property (which is just about everything other than a Button)?
Should LooklessControl have it's own XAML file (much like what you get when you create a new UserControl) associated with it that Generic.xaml just pulls in as a MergedDictionar as its default template? Or is there some other acknowledged way to do what I'm trying to do?

To answer your last question: NO. The lookless control shouldn't require any known XAML. That is what lookless means.
You have a couple of options here, but I would recommend wrapping your elements in Buttons with a basically empty control template:
<ControlTemplate x:Key="contentOnlyButton" TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
...
<Button Grid.Column="0" Template="{StaticResource contentOnlyButton}"
Command="{x:Static local:LooklessControl.PrevItemCommand}">
<Path x:Name="pathLeftArrow" Data="M0,0.5 L1,1 1,0Z" Width="6" Height="14"
Stretch="Fill" HorizontalAlignment="Center" Fill="SlateBlue"/>
</Button>
Your other option (and I would say this is probably not what you should do for executing commands on clicks, but may be applicable in other circumstances), would be to look for the named part in your template in OnApplyTemplate, and wire up the events.
public override void OnApplyTemplate()
{
var prevElement = this.GetTemplateChild("PART_PathLeftArrow") as UIElement;
if (prevElement != null)
prevElement.MouseDown += (o, e) => PrevItemHandler();
...
}
One thing to note with doing this is that the Template isn't required to define the parts you are looking for, so you need to gracefully check for that circumstance. Throwing NullReferenceExceptions here will make restyling your control a royal pain for designers / developers who accidentally delete a required element. You will also want to follow the standard practice of naming your required elements with a PART_ syntax, and decorating your class with TemplatePart attributes.
[TemplatePart(Name = "PART_PathLeftArrow", Type = typeof(UIElement))]
[TemplatePart(Name = "PART_PathRightArrow", Type = typeof(UIElement))]
...
public class LooklessControl : Control
Edit: In order for the Button's to respond to the clicks, you need to setup CommandBindings to your functions that you had already defined. You would do this as a class command binding like so:
static LooklessControl()
{
CommandManager.RegisterClassCommandBinding(
typeof(LooklessControl),
new CommandBinding(NextItemCommand, ExecutedNextItemCommand, CanExecuteNextItemCommand));
CommandManager.RegisterClassCommandBinding(
typeof(LooklessControl),
new CommandBinding(PrevItemCommand, ExecutedPrevItemCommand, CanExecutePrevItemCommand));
}
The reason to do a class command binding is that if you add it to your control's CommandBindings collection, somebody using your control could inadvertently remove them. Also remember to update your command handling methods to have static semantics.

Related

WPF DepenencyProperty logic in code behind

Currently my CustomControl adjust his element color affectionate of the status received from the PLC. The CustomControl haves two dependency property: Statebrush and InstanceAdresFFU. As you can see, the InstanceAdres has the same starting adres as the StateBrush adres.
MainView1.xaml
<cc:CustomControl1 x:Name="FFU_2" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="4" Margin="96,243,0,0" Width="44" Height="30" BorderThickness="1"
StateBrush="{vw:VariableBinding VariableName=MCS1.Cleanroom.SIM_Cleanroom.SIM_FFUControl2.Observers.oStatus, Converter={StaticResource ValueToStateBrushConverter}, States={StaticResource BrushListState}, StateMode=Value}"
InstanceAdresFFU="MCS1.Cleanroom.SIM_Cleanroom.SIM_FFUControl2"/>
Therefore I want to get rid of the StateBrush binding in the MainView.
<cc:CustomControl1 x:Name="FFU_2" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="4" Margin="96,243,0,0" Width="44" Height="30" BorderThickness="1"
InstanceAdresFFU="MCS1.Cleanroom.SIM_Cleanroom.SIM_FFUControl2"/>
And in the code behind, something like this:
StateBrush= this.InstanceAdresFFU + ".Observers.oStatus", Converter={StaticResource ValueToStateBrushConverter}, States={StaticResource BrushListState}, StateMode=Value}"
So my question is, is it possible (and how) to add a default variable to a depency property in the backhand, with its associated Converter={StaticResource ValueToStateBrushConverter}, States={StaticResource BrushListState}, StateMode=Value}"
CustomControl1.cs
namespace HMI.CustomControl
{
public class CustomControl1 : System.Windows.Controls.Button
{
public Brush StateBrush
{
get { return (Brush)GetValue(StateBrushProperty); }
set { SetValue(StateBrushProperty, value); }
}
// Using a DependencyProperty as the backing store for StateBrush. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StateBrushProperty =
DependencyProperty.Register(nameof(StateBrush), typeof(Brush), typeof(CustomControl1), new PropertyMetadata(default(Brush)));
public string InstanceAdresFFU
{
get { return (string)GetValue(InstanceAdresFFUyProperty); }
set { SetValue(InstanceAdresFFUyProperty, value); }
}
// Using a DependencyProperty as the backing store for InstanceAdresFFU. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InstanceAdresFFUyProperty =
DependencyProperty.Register("InstanceAdresFFU", typeof(string), typeof(CustomControl1), new PropertyMetadata(""));
}
}
Generic.xaml : Frontend of object
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:HMI.CustomControl" xmlns:vw="http://inosoft.com/visiwin7">
<Style TargetType="{x:Type cc:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cc:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="19*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<vw:Label Margin="5" Text="{TemplateBinding LabelText}" Height="40" Focusable="False"/>
<Rectangle Fill="{TemplateBinding StateBrush}" Width="40" Height="24" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1,1,0,0" Grid.ColumnSpan="6"/>
<Line Stroke="#FF000000" Height="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1,4,0,0" Width="40" Y1="0" Y2="0.114682539682633" X2="39.5" StrokeThickness=".5" Grid.ColumnSpan="6"/>
<Line Stroke="#FF000000" Height="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1,1,-2,0" Width="43" Y1="0" Y2="0.114682539682633" X2="39.5" Grid.ColumnSpan="6"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Due to the lack of valuable information I will show you some very general examples to give you an idea.
You must register a property change callback and handle the InstanceAdresFFU value and the ValueBinding markup extension.
The example assumes that the referenced resources are defined in the App.xaml resources.
If VariableBinding extends BindingBase
public class CustomControl1 : Button
{
public string InstanceAdresFFU
{
get => (string)GetValue(InstanceAdresFFUyProperty);
set => SetValue(InstanceAdresFFUyProperty, value);
}
// Using a DependencyProperty as the backing store for InstanceAdresFFU. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InstanceAdresFFUyProperty = DependencyProperty.Register(
"InstanceAdresFFU",
typeof(string),
typeof(CustomControl1),
new PropertyMetadata(default(string), OnInstanceAdresFFUChanged));
private static void OnInstanceAdresFFUChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as CustomControl1).OnInstanceAdresFFUChanged();
public MultiSelectComboBox() => this.Loaded += OnLoaded;
private void OnLoaded(object sender, RoutedEventArgs e)
=> OnInstanceAdresFFUChanged();
protected virtual void OnInstanceAdresFFUChanged()
{
if (string.IsNullOrWhiteSpace(this.InstanceAdresFFU))
{
return;
}
IValueConverter converter = (IValueConverter)Application.Current.Resources["ValueToStateBrushConverter"];
object states = Application.Current.Resources["BrushListState"];
var binding = new VariableBinding
{
VariableName = $"{this.InstanceAdresFFU}.Observers.oStatus",
Converter = converter,
States = states,
StateMode = "Value"
}
SetBinding(StateBrushProperty, binding);
}
}
If VariableBinding does not extend BindingBase
public class CustomControl1 : Button
{
public string InstanceAdresFFU
{
get => (string)GetValue(InstanceAdresFFUyProperty);
set => SetValue(InstanceAdresFFUyProperty, value);
}
// Using a DependencyProperty as the backing store for InstanceAdresFFU. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InstanceAdresFFUyProperty = DependencyProperty.Register(
"InstanceAdresFFU",
typeof(string),
typeof(CustomControl1),
new PropertyMetadata(default(string), OnInstanceAdresFFUChanged));
private static void OnInstanceAdresFFUChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as CustomControl1).OnInstanceAdresFFUChanged();
public MultiSelectComboBox() => this.Loaded += OnLoaded;
private void OnLoaded(object sender, RoutedEventArgs e)
=> OnInstanceAdresFFUChanged();
protected virtual void OnInstanceAdresFFUChanged()
{
if (string.IsNullOrWhiteSpace(this.InstanceAdresFFU))
{
return;
}
IValueConverter converter = (IValueConverter)Application.Current.Resources["ValueToStateBrushConverter"];BrushListState
object states = Application.Current.Resources["BrushListState"];
var markupExtension = new VariableBinding
{
VariableName = $"{this.InstanceAdresFFU}.Observers.oStatus",
Converter = converter,
States = states,
StateMode = "Value"
}
this.StateBrush = markupExtension.ProvideValue(new ServiceProviders());
}
}

How to bind/wrap an event of a child control to custom control's event, in C#?

I'm building a Custom Control (or Templated, if you mind), but I can't figure out how to bind the event (the Click) of a button inside the custom control to the Click event of the Custom Control itself.
I have searched on the internet, but some solution was only for WPF (including classes not available in the UWP platform), some was for Visual Basic, some other wasn't exactly my case and so on...
Here is the code, that works perfectly so far, for best clearance (please note, I have changed the project's and namespace's name to hide it, putting instead "SomeClass"):
The custom control, IconButton.cs:
public sealed class IconButton : Control
{
public IconButton()
{
this.DefaultStyleKey = typeof(IconButton);
}
public Boolean IconButtonIsLabelVisible
{
get { return (Boolean)GetValue(IconButtonIsLabelVisibleProperty); }
set { SetValue(IconButtonIsLabelVisibleProperty, value); }
}
public static readonly DependencyProperty IconButtonIsLabelVisibleProperty =
DependencyProperty.Register("IconButtonIsLabelVisible", typeof(Boolean), typeof(IconButton), new PropertyMetadata(true));
public String IconButtonLabel
{
get { return (String)GetValue(IconButtonLabelProperty); }
set { SetValue(IconButtonLabelProperty, value); }
}
public static readonly DependencyProperty IconButtonLabelProperty =
DependencyProperty.Register("IconButtonLabel", typeof(String), typeof(IconButton), new PropertyMetadata("Content"));
public Double IconButtonLabelMargin
{
get { return (Double)GetValue(IconButtonLabelMarginProperty); }
set { SetValue(IconButtonLabelMarginProperty, value); }
}
public static readonly DependencyProperty IconButtonLabelMarginProperty =
DependencyProperty.Register("IconButtonLabelMargin", typeof(Double), typeof(IconButton), new PropertyMetadata(10));
public Style IconButtonStyle
{
get { return (Style)GetValue(IconButtonStyleProperty); }
set { SetValue(IconButtonStyleProperty, value); }
}
public static readonly DependencyProperty IconButtonStyleProperty =
DependencyProperty.Register("IconButtonStyle", typeof(Style), typeof(IconButton), new PropertyMetadata(null));
public IconElement IconButtonIcon
{
get { return (IconElement)GetValue(IconButtonIconProperty); }
set { SetValue(IconButtonIconProperty, value); }
}
public static readonly DependencyProperty IconButtonIconProperty =
DependencyProperty.Register("IconButtonIcon", typeof(IconElement), typeof(IconButton), new PropertyMetadata(0));
}
The generic xaml template file, Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SomeClass.Controls">
<Style TargetType="local:IconButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:IconButton">
<Button x:Name="ClickButton" Style="{TemplateBinding IconButtonStyle}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Command="{TemplateBinding Command}" CommandParameter="{TemplateBinding CommandParameter}">
<Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Content"
Content="{TemplateBinding IconButtonIcon}"
Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center"/>
<Grid Grid.Column="1" Width="{TemplateBinding IconButtonLabelMargin}"/>
<TextBlock Grid.Column="2" Text="{TemplateBinding IconButtonLabel}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center"/>
</Grid>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the MainPage.xaml, where I would like to use the IconButton:
<Page
x:Class="SomeClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SomeClass"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:testControls="using:SomeClass.Controls"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<testControls:IconButton x:Name="TestButton" Click"?" IconButtonLabelMargin="5" HorizontalAlignment="Center" Foreground="Aqua" VerticalAlignment="Center" Background="Transparent" >
<testControls:IconButton.IconButtonIcon>
<SymbolIcon Symbol="Preview"/>
</testControls:IconButton.IconButtonIcon>
</testControls:IconButton>
</Grid>
So, given this code, I would like to bind in some way the Click event of the ClickButton in
the xaml template of the IconButton to the default Click event of the
IconButton control itself, so that it can be easily used in the mainpage by
simply specifying the Click event.
Thank you for you kindness and your attention.
Best regards.
Doing this requires overriding the OnApplyTemplate method in your control, finding the named template part in your control, and raising the event on your wrapper.
Inside your custom control:
ButtonBase clickButtonPart = null;
public const string ClickButtonTemplatePartName = "ClickButton";
public event EventHandler Click;
protected override void OnApplyTemplate()
{
// In case the template changes, you want to stop listening to the
// old button's Click event.
if (clickButtonPart != null)
{
clickButtonPart.Click -= ClickForwarder;
clickButtonPart = null;
}
// Find the template child with the special name. It can be any kind
// of ButtonBase in this example.
clickButtonPart = GetTemplateChild(ClickButtonTemplatePartName) as ButtonBase;
// Add a handler to its Click event that simply forwards it on to our
// Click event.
if (clickButtonPart != null)
{
clickButtonPart.Click += ClickForwarder;
}
}
private void ClickForwarder(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Click?.Invoke(this, null);
}

WPF Validating TextBox in an outside library

what I am trying to do is somewhat out there, and I have yet to really see an example of this.
I am trying to validate a textbox entry that is essentially a required field (it cannot be null or empty). However, I do not have any access to the code behind, only to the XAML and data binding for the form.
From searching for a couple of days, I found out this cannot be done strictly in XAML (which would have been preferred), and had to create my own resource library to check for this. That is what I have done, but failed to get it to work.
Is this even a possibility? Or what would I have to do to get this to work?
What I have done so far was create a usercontrol template of a textbox to then use in the XAML (residing in an outside library):
<UserControl.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="MyAdorner"/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<DockPanel x:Name="dpMain" LastChildFill="True">
<Label/>
</DockPanel>
</Grid>
And the code behind:
namespace ClassLibrary.CustomControls
{
public partial class CssTextBox : UserControl
{
private TextBox _textbox = null;
private ObservableCollection<ValidationRule> _validationRules = null;
public CssTextBox()
{
InitializeComponent();
CreateControls();
ValidationRules = new ObservableCollection<ValidationRule>();
this.DataContextChanged += new DependencyPropertyChangedEventHandler(CssTextBoxDataChanged);
}
public ObservableCollection<ValidationRule> ValidationRules
{
get { return _validationRules; }
set { _validationRules = value; }
}
private void CreateControls()
{
_textbox = new TextBox() { Width = 100, Height = 20 };
_textbox.LostFocus += CssTextBoxLostFocus;
_textbox.Style = TextBoxErrorStyle;
}
public void CssTextBoxDataChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_textbox != null)
{
var binding = new Binding();
binding.Source = this.DataContext;
binding.ValidatesOnDataErrors = true;
binding.ValidatesOnExceptions = true;
foreach (var rule in ValidationRules)
{
binding.ValidationRules.Add(rule);
}
binding.Path = new PropertyPath(BoundPropertyName);
_textbox.SetBinding(TextBox.TextProperty, binding);
dpMain.Children.Add(_textbox);
}
}
public void CssTextBoxLostFocus(object sender, RoutedEventArgs e)
{
var bindingExpression = _textbox.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null)
bindingExpression.UpdateSource();
}
private Style TextBoxErrorStyle
{
get
{
return (Style)FindResource("TextBoxStyle");
}
}
public string TextBoxErrorStyleName { get; set; }
public string BoundPropertyName { get; set; }
public string ValidationExpression { get; set; }
public string Text
{
get
{
return _textbox.Text;
}
}
public string ErrorText { get; set; }
}
And how it is being used (currently being tested in a WPF Sandbox project and only being referenced via XAML):
xmlns:css="clr-namespace:WpfSandbox.CustomControls" <!--Reference to library that holds above--!>
<css:CssTextBox TextBoxErrorStyleName="TextBoxStyle" Grid.Column="0" Grid.Row="1" Width="100" Height="20" VerticalAlignment="Top" >
<css:CssTextBox.ValidationRules>
<validators:NotNullOrEmptyValidationRule ErrorMessage="Cannot be Empty!" />
</css:CssTextBox.ValidationRules>
</css:CssTextBox>
<TextBox Grid.Column="0" Grid.Row="2" Width="auto" Height="20" VerticalAlignment="Top" Background="White" IsEnabled="True"/>
My issue with what I have now, is that it shows the textbox in my designer window in my sandbox application, but I cannot click into it when I run. It's almost like it does not exist.
Thanks for any insight!
You should read about WPF Data validation.
This link will help you:
https://msdn.microsoft.com/fr-fr/library/system.componentmodel.idataerrorinfo(v=vs.95).aspx

MVVM+ WPF PopUp not opening

XAML
<Popup Name="popUpProgress" Width="225" Height="85"
IsOpen="{Binding PopUpIsOpen,Mode=OneWay}"
Placement="Center" PlacementTarget="{Binding ElementName=stkListview}"
VerticalAlignment="Top">
<Border BorderThickness="1" Background="Blue" >
<Grid Width="225" Height="85">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label x:Name="lblProgress" Content="Please Wait ...." Margin="10,5,0,0" HorizontalAlignment="Left" Grid.Row="1" />
</Grid>
</Border>
</Popup>
In view Model:
private bool _PopUpIsOpen;
public bool PopUpIsOpen
{
get { return _PopUpIsOpen; }
set
{
_PopUpIsOpen = value;
RaisePropertyChanged(() => this.PopUpIsOpen);
}
}
public RelayCommand SubmitCommand { get; private set; }
private bool SubmitCommandCanExecute()
{
return true;
}
private void SubmitCommandExecute()
{
PopUpIsOpen = true;
dsStandardListbyMarket = marketBL.StandardListbyMarketBL(Convert.ToInt32(SelectdMarketId), Convert.ToInt32(Users.UserId));
GetComboboxMappingCollections(Convert.ToInt32(this.SelectdMarketId), Users.UserId);
FItems = new ObservableCollection<MarketRecord.FItem>();
FItems.CollectionChanged += OnUICollectionChanged;
marketBL.FetchMarketRecords(Convert.ToInt32(this.SelectdMarketId));
IsSubmitButtonVisible = true;
PopUpIsOpen = false;
}
When I click on submit button control comes to SubmitCommandExecute but Popup window is not showing. I am bit new to WPF, scratching my head over it. Finally raising this question here. What might be wrong.
I think the problem is in the way you are testing the code. SInce you are sleeping in the UI thread, the UI does not feel the change from true to false on the bound property.
Try to use a timer instead of a Sleep in the thread.
Given the RaisePropertyChanged syntaxe on msdn :
protected internal void RaisePropertyChanged (
string propertyName
)
You should try RaisePropertyChanged("PopUpIsOpen"); instead of RaisePropertyChanged(() => this.PopUpIsOpen);

Assigning an event or command to a DataTemplate in ResourceDictionary

I have the following class:
public class Day
{
public int Date { get; set; }
public String DayName { get; set; }
public Day()
{
}
public Day(int date, string dayName)
{
Date = date;
DayName = dayName;
CommandManager.RegisterClassCommandBinding(typeof(Day), new CommandBinding(DayClick, new ExecutedRoutedEventHandler(OnExecutedDayClick),
new CanExecuteRoutedEventHandler(OnCanExecuteDayClick)));
}
public static readonly RoutedCommand DayClick = new RoutedCommand("DayClick", typeof(Day));
private static void OnCanExecuteDayClick(object sender, CanExecuteRoutedEventArgs e)
{
((Day)sender).OnCanExecuteDayClick(e);
}
private static void OnExecutedDayClick(object sender, ExecutedRoutedEventArgs e)
{
((Day)sender).OnExecutedDayClick(e);
}
protected virtual void OnCanExecuteDayClick(CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = false;
}
protected virtual void OnExecutedDayClick(ExecutedRoutedEventArgs e)
{
string content = String.Format("Day {0}, which is {1}, was clicked.", Date.ToString(), DayName);
MessageBox.Show(content);
e.Handled = true;
}
}
I am using the following DataTemplate (that is in a ResourceDictionary) to render it:
<DataTemplate DataType="{x:Type local:Day}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.ColumnSpan="2" x:Name="rectHasEntry" Fill="WhiteSmoke"/>
<TextBlock Grid.Column="0" x:Name="textBlockDayName" Text="{Binding DayName}"
FontFamily="Junction" FontSize="11" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
<TextBlock Grid.Column="1" x:Name="textBlockDate" Text="{Binding Date}"
FontFamily="Junction" FontSize="11" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
<Rectangle Grid.ColumnSpan="2" x:Name="rectMouseOver" Fill="#A2C0DA" Opacity="0"
Style="{StaticResource DayRectangleMouseOverStyle}">
</Rectangle>
</Grid>
</DataTemplate>
No problems so far, I can get it on screen.
What I want to be able to do is assign a Command, or use an event, so that when the user clicks on the Day it will notify the parent of the Day object that it has been clicked.
I've tried the following:
<Rectangle.CommandBindings>
<CommandBinding Command="{x:Static local:Day.NextDay}"
Executed="{x:Static local:Day.OnExecutedDayClick}"
CanExecute="{x:Static local:Day.OnCanExecuteDayClick}"/>
</Rectangle.CommandBindings>
to try and bind the commands that are in the Day class but it didn't work. I got an error stating:
'ResourceDictionary' root element requires a x:Class attribute to support event handlers in the XAML file. Either remove the event handler for the Executed event, or add a x:Class attribute to the root element.
Which I think means that there is no code-behind file for a ResourceDictionary, or something to that effect.
In any event, I'm not sure if I should be using Commands here, or somehow tying events to the Rectangle in question, or if this is even possible. I've seen various places where it sure looks like it's possible, I'm just unable to translate what I'm seeing into something that actually works, hence this post.
Thanks in advance.
You cann't declare CommandBinding here, in this case you can assign the command here in DataTemplate and declare CommandBinding in your main Window or Page.
Edit:
In this way you can use Commands with your custom control.
Create a custom control and Declare Commands and Command Bindings also inside the control itself as in this Sample.
MyCustomControl.cs
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
InitializeCommands();
}
private static RoutedCommand _myCommand;
public static RoutedCommand MyCommand
{
get
{
return _myCommand;
}
}
private static void InitializeCommands()
{
_myCommand = new RoutedCommand("MyCommand", typeof(MyCustomControl));
CommandManager.RegisterClassCommandBinding(typeof(MyCustomControl),
new CommandBinding(_myCommand , OnMyCommandExecute));
}
private static void OnMyCommandExecute(object sender, ExecutedRoutedEventArgs e)
{
MyCustomControl control = sender as MyCustomControl;
if (control != null)
{
//logic for this command
}
}
and in your generic.xaml write this style and assign commands like this:
generic.xaml
<Style TargetType="{x:Type local:MyCustomControl}">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid>
<RepeatButton Command="{x:Static local:MyCustomControl.MyCommand}" >Click</RepeatButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Categories