WPF uses and expected results of AccessText class - c#

I was going through the class AccessText today. I couldn't identify exact use and results of using this class.
If you use AccessText with a Label and use Target property as a
TextBox, the TextBox will get focus when access key is pressed.see below code:
<StackPanel>
<Label Target="{Binding ElementName=txtbox}">
<AccessText>_first_second</AccessText>
</Label>
<TextBox Name="txtbox" Width="100" Height="50"/>
</StackPanel>
If you use AccessText with a Label and use Target property as a
Buton , the Button Click event will get executed when access
key is pressed.
So my questions are
1.what is the definitive behavior of AccessText Class? If I have to predict it's uses with other type of controls like DataGrid, ComboBox,
RadioButton? how can i be sure of expected result?
2.Why is this class derived from FrameworkElement? What applications does it have as a FrameworkElement? seems a bit more for
just specifying Accesskeys etc.

AccessText is a FrameworkElement that acts more or less like a special type of TextBlock that allows any keyboard character following a single underscore (_) to act as an access key.
For a given control, the behavior of associated access keys depends on its OnAccessKey method. OnAccessKey is a virtual method of UIElement, which provides the following definition:
protected virtual void OnAccessKey(AccessKeyEventArgs e)
{
this.Focus();
}
So, any control that doesn't override the definition of OnAccessKey defined by UIElement will maintain the default behavior, which is for the control to be brought into focus when the access key is pressed.
ButtonBase, which Button inherits from, has the following definition for OnAccessKey:
protected override void OnAccessKey(AccessKeyEventArgs e)
{
if (e.IsMultiple)
base.OnAccessKey(e);
else
this.OnClick();
}
So the default behavior of Button and other controls that inherit from ButtonBase will be to bring the control into focus if IsMultiple is true, otherwise, it will raise the click event. (IsMultiple is true if an access key is associated with more than one UIElement.)
With this background in mind, here are the answers to your specific questions:
The definitive behavior of an AccessText element used as a control's ContentPresenter is to register the first letter following a single underscore with the AccessKeyManager, which will invoke the control's OnAccessKey method when the key is pressed. Knowing what this will do for a particular control requires knowing which definition of OnAccessKey is in force for that control. If there are no overrides in its inheritance chain, pressing the access key will bring the control into focus. If there is an override, the behavior will depend on the overriding method's definition. This can be determined via experimentation, reading relevant documentation, or examining the source code.
AccessText is a FrameworkElement for the same reasons that TextBlock is a FrameworkElement. It has a visual form and takes up space that the layout system needs to take into account when positioning other elements relative to it. Also, FrameworkElements allow for styling, and they possess their own DataContext property, which allows for binding scenarios that would otherwise not be possible. If AccessText were not a FrameworkElement, it would be unnecessarily limiting and prevent reasonable (though perhaps rare) use cases WPF developers may have.
Edit
Here's an example of a fancy power button that demonstrates the usefullness of AccessText being a FrameworkElement:
<Grid>
<Button Width="150"
Height="35"
Command="{Binding PowerCommand}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Status" />
<Rectangle Margin="5,2,0,0"
Width="10"
Height="10"
Fill="{Binding PowerFill}" />
<AccessText Margin="25,0,0,0"
Text="{Binding PowerText}" />
</StackPanel>
</Button>
</Grid>
This results in (after pressing Alt):
After clicking the button, or pressing Alt+S, the view model would respond to the command by changing the Text and Fill, resulting in this:
Clicking or using the access key again would return to the first state.

Related

Add EventListener to WPF control defined in Generic.xaml [duplicate]

I have a WPF control ParentWPFControl from a third party that I would like to inherit from (let's call the child class ChildWPFControl). In the process, I plan to override some of the back-end logic and parts of the front end styles. I can do the former just fine but I have problems doing the latter.
I attempt to use a xaml <-> xaml.cs structure for the child country, but that appears to be not allowed with the following warning from VS:
Partial declarations of 'ChildWPFControl' must not specify different base classes
Now, I suppose I can write a ResourceDictionary XAML and define the front end there, but that becomes a problem if I want to add event handlers to the XAML (at least I couldn't find a way to do that)
Another alternative I have is to define the override template directly in the objects that use the ChildWPFControl but that makes the design less modular.
A final alternative I can think of is to make a xaml <-> xaml.cs pair that is a XAML style container and then force the ChildWPFControl to use the ControlTemplate defined within through the back end event handler.
Anyway, what I am looking for is an elegant and modular solution for my problem. Any advice would be welcomed.
Thanks
There are a couple of steps necessary to completely override a WPF Control. Some are necessary some are optional depending on your needs. I will explain the two important ones for you:
Creating a new default style
Every WPF control has somewhere a default style which contains it visual representation and override properties. Now if you derive from control WPF still thinks you want to use this default style, to change that you change the DefaultStyle in a static constructor like this
class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
Now if you use MyButton WPF tries to find a Style for MyButton, not for Button. OverridesDefaultStyle is a property in a style which might also be handy at some points. Usually these default styles should be placed in a theme related xaml.
Event Handlers when overriding classes
It is correct in a ControlTemplate or Style you can't use the syntactic sugar of using event like Click="OnClick". The point is, that the visual representation is decoupled from the logic part as much as possible. There are other ways though to overcome this, using the OnApplyTemplate method. By overriding this you ask the template "Give me this control" and then you just add your events there.
override OnApplyTemplate()
{
var innerChild = Template.FindName("PART_InnerChild", this) as MyInnerControl;
if(innerChild != null)
innerChild.SomeEvent += OnSomeEvent;
}
Note: The name of these controls usually begin with a PART_ by convention, this can be seen in WPF basic controls aswell. Its a nice way to tell the designers "Without this control, the logic part might break". There is also the attribute TemplatePart but it is not really important, WPF doesn't care about it. AFAIK Expression blend does some with it, personally i use it to tell other people what kind of inner controls are absolutely necessary to make this control work.
Personal advice
Deriving from a class is usually the last step we do when trying to customize controls. Because a lot of work is necessary to fully make it work and it can be limiting in reusability, we try to avoid it, for example a good alternatives are besides template overriding and styling; attached behaviors.
Lastly,
The whole subject is covered in a nice MSDN article.
Hope that helps
You can create your user control as wrapper, containing base control. In this way you can change styles in xaml add some logic in C# for wrapped contrоl. But it's tediously process.
Edit:adding sample(wrapper for telerik:RadComboBox )
XAML:
<UserControl x:Class="Controls.SingleDictionaryValueSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CardControls="clr-namespace:Controls"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" MinWidth="150" MinHeight="25" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="25"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- customize visual for wrapped control -->
<telerik:RadComboBox x:Name="cb"
Grid.Column="0"
VerticalAlignment="Center"
SelectedValuePath="Key"
ClearSelectionButtonContent="Clear"
ClearSelectionButtonVisibility="Visible"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
SelectAllTextEvent="None"
OpenDropDownOnFocus="false"
IsFilteringEnabled="True"
TextSearchMode="Contains"
EmptyText="Select item"
telerik:StyleManager.Theme="Metro"
FontFamily="Calibri"
FontSize="14"
IsEditable="True"
Foreground="#666"
KeyDown="cb_KeyDown"
SelectionChanged="cb_SelectionChanged"
GotMouseCapture="cb_GotMouseCapture"
DropDownOpened="cb_DropDownOpened"
KeyUp="cb_KeyUp">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadComboBox},Path=ActualWidth}" Text="{Binding Path=Value}" />
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
<CardControls:ErrorInfo x:Name="errorInfoControl" Grid.Column="1" Visibility="Hidden"></CardControls:ErrorInfo>
</Grid>
</UserControl>
CS:
public partial class SingleDictionaryValueSelector : IMyCustomInterface
{
....
private void cb_KeyDown(object sender, KeyEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_KeyUp(object sender, KeyEventArgs e)
{
SearchExecute();
}
private void cb_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_DropDownOpened(object sender, EventArgs e)
{
...
}
...
}
It looks like you have your inheritance mixed up more than that it is not allowed. Your root element of your xaml must match the base class of your xaml.cs.
If you are defining the base class in the same project, you will not be able to use it as the base class in the xaml, because it itself is still xaml and not a compiled control yet. Some ways to solve this: You can compile it in a seperate project and reference it, you can compile the base class entirely in .cs instead of a partial class, or you can use some style wizardry. Here is a link with examples of the last two: http://svetoslavsavov.blogspot.ca/2009/09/user-control-inheritance-in-wpf.html

Why are my custom controls not always receiving MouseEnter events?

Alright, I'm fairly new to WPF and I ran into a very strange problem. The relevant section of my XAML defines a Border around a ScrollViewer around a StackPanel which is populated using an ItemsControl that is then databound to a CollectionViewSource which in turn wraps a standard ObservableCollection. The ItemsControl defines a DataTemplate that contains only one tag: a custom control I've made called a StackElement. I'm handling three events from this control — MouseEnter, MouseLeave, and PreviewMouseLeftButtonUp. These events can fire, but do so unreliably.
For example, after some new StackElements are added, the MouseEnter event generally doesn't fire on the first StackElement until I've moused over a few others. Once a MouseOver manages to fire once, it continues to fire correctly on that StackElement from there on out.
However, the first time mousing over a StackElement doesn't always fail. If I approach the StackElements from beneath and try the last one first, it will always fire. When I do this, sometimes the first one will work, but the second one won't fire. Once, both of them did manage to operate correctly, but it happens infrequently.
I'm not multithreading anything, none of my parent controls handle events of their own, all event handlers consist only of a WriteLine() statement for debugging purposes, and the StackElement code-behind isn't handling any events either.
I've tried decoupling the ItemsControl from the CollectionViewSource in favor of binding it directly to the ObservableCollection, which did nothing other than (as I expected) bypass the sorting functionality I added to the ViewSource. I tried handling the events in the StackElement class itself, in addition to making them be tied to other controls contained within StackElement. I tried using DataTriggers, which if I remember worked as expected, but I need to include more advanced logic such as multiselection and the inability to lightly highlight an already-selected StackElement.
For context, I'm intending to use these events to lightly highlight StackElements when the user drags the mouse over them and to strongly highlight them when the mouse is pressed — basically, I need something that looks and feels like Windows File Explorer. From what I've seen this can't be accomplished in an elegant fashion with DataTriggers alone.
Here's my event handlers (in MainWindow.xaml):
private void StackElement_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("OnPreviewMouseLeftButtonUp fired for a StackElement.");
}
private void StackElement_OnMouseEnter(object sender, MouseEventArgs e)
{
Console.WriteLine("OnMouseEnter fired for a StackElement.");
}
private void StackElement_OnMouseLeave(object sender, MouseEventArgs e)
{
Console.WriteLine("OnMouseLeave fired for a StackElement.");
}
Here's how I'm adding to the bound collection (for testing, which is why it's hooked up to a random button):
private void Btn_File_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
InitiativeStackElement t = new InitiativeStackElement(new Entity("TestName", 10, 11, 12, 13, null)); //InitiativeStackElement implements INotifyPropertyChanged so the databindings work
_entityProvider.Elements.Add(t); //_entityProvider is just a reference to a XAML-defined resource class, which is loaded up in the constructor so I don't have to call TryGetResource() whenever I want to use it. it's currently used for testing purposes only
}
Finally, here's the portion of my XAML containing the StackElements:
<Border Grid.Row="1"
Margin="0,1,0,0"
Style="{StaticResource StandardBorder}">
<ScrollViewer Name="Scv_InitiativeStack">
<StackPanel Name="Stp_InitiativeStack">
<ItemsControl Name="Its_InitiativeStack" ItemsSource="{Binding Source={StaticResource SortedInitiativeStack}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<con:StackElement Element="{Binding}" PreviewMouseLeftButtonUp="StackElement_OnPreviewMouseLeftButtonUp" MouseEnter="StackElement_OnMouseEnter" MouseLeave="StackElement_OnMouseLeave"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Border>
The StackElement class just defines a single DependencyProperty of type InitiativeStackElement. The properties of this object are bound to a few controls within the StackElement, which always displays correctly. It's the behavior of the events that have me confused.
As described, I'm expecting the MouseEnter event to fire whenever the mouse is dragged onto the StackElement. However, it's only firing after I fulfill seemingly random conditions that shouldn't affect it's functionality, like mousing over another StackElement first. There are no error messages.
Alright, I was able to get the functionality I wanted using ListBox:
<Window.Resources>
<DataTemplate x:Key="InitiativeStackTemplate">
<con:StackElement Element="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Border Margin="0,1,0,0"
Grid.Row="1"
Style="{StaticResource StandardBorder}">
<ScrollViewer Name="Scv_InitiativeStack">
<ListBox Name="Lbx_InitiativeStack"
SelectionMode="Extended"
ItemsSource="{Binding Source={StaticResource SortedInitiativeStack}}"
ItemTemplate="{StaticResource InitiativeStackTemplate}"
HorizontalContentAlignment="Stretch"/>
</ScrollViewer>
</Border>
Everything works as expected.

How to handle Click event on a custom WPF control composed of controls that don't have Click events

I am creating a custom ToolBar control that I will be able to use in my WPF application.
My CustomToolBar control is based on a StackPanel control and will contain multiple CustomToolBarButton controls.
My CustomToolBarButtons are based on a vertical StackPanel and contain an Image control and a Label control.
I want to programmatically create a Click event for my CustomToolBarButtons that will fire when Image or label gets clicked. Unfortunately, neither Image nor Label controls have Click events in WPF.
It's a surprise for me because I am used to WinForms controls and vast majority of them have Click events by default. Do I have to create custom Image and Label controls and create Click events for them or is there a cleaner and simpler way of doing this?
Thanks for any help!
Well, there's not a great and simple way using standard functionality. I got around this by making my own trigger (derived from System.Windows.Interactivity.TriggerBase from the Blend SDK) so that in my projects I can do the following:
<Label>
<i:Interaction.Triggers>
<mu:MouseTrigger MouseButton="Middle" MouseAction="Click">
<i:InvokeCommandAction Command="{Binding Path=Close}" />
</mu:MouseTrigger>
</i:Interaction.Triggers>
</Label>
Effectively the MouseTrigger class will handle MouseDown and MouseUp events from the UIElement it's attached to and use that to invoke the actions associated with the trigger. The code is a bit more complex than just that, though, since I also do mouse capturing & I utilize an internal helper class so that I could add multiple triggers to the same element with only one instance of a helper class handling events & capturing the mouse for that element.
For the actual actions, I just use existing Blend or Prism actions such as InvokeCommandAction.
Here is the project if you're interested. It was too large to paste into this format. It uses some C# 6.0 features but you could easily modify it to work on an older version of C# by removing some null conditional operators. It requires you install the Blend SDK as it depends on System.Windows.Interactivity (should install with Visual Studio as long as you select that option). MouseTrigger is the publicly visible class that is the point of interaction with the functionality. MouseCatcher is the internal helper class mentioned.
I'd recommend not going down the route of custom controls, but rather of using some mechanism (this one or otherwise) to extend the existing functionality using the attached property framework that WPF and XAML bring to the table.
In WinForms you would create custom controls simply to get a nonstandard look, in WPF this is no longer necessary.
The WPF way: instead of making a Stackpanel's children implement Click behaviour, let's make a Button look like the desired Stackpanel.
Could do this setting the Content:
<Button>
<StackPanel >
<Image Source="C:\myFiles\myPic.png"/>
<Label HorizontalAlignment="Center">SomeTxt</Label>
</StackPanel>
</Button>
But this way it still looks like a Button, to overcome this we can set the Template instead:
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<StackPanel >
<Image Source="C:\myFiles\myPic.png"/>
<Label HorizontalAlignment="Center">SomeTxt</Label>
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
Both solutions will raise a Click event clicking the image or label.

Do I need dependency properties for all basic properties on my user control?

I am currently writing my first user control which would consist of a label and a text box in a stack panel like follows:
<Grid>
<StackPanel Orientation="Horizontal" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Label Content="{Binding Label}" Width="60"></Label>
<TextBox Text="{Binding TextBoxContent}" Width="60"/>
</StackPanel>
</Grid>
This will be most useful to be in a settings page, as it will be reused for several different settings. With each of these settings, I will want to set (at a minimum) the width, height, validation rule and error template properties. As for the text itself, I have already created a dependency property both for the label and the text box (as you can see in my snippet above).
My question is this: Do I need to create a dependency property for all of the properties I just mentioned that I would like to set when I actually use my user control? This seems like redundant work (since they already exist on the text box, basically they would just redirect my user control's property to the text box's property of the same name)? This is even more work if I want to use even more properties on my text box (for example, AcceptsReturn, etc).
The redundant work can be saved if you decide to derive from TextBox rather than UserControl - just think of your control as a "labeled textbox" and all you need to do is derive from TextBox and add the needed dependency properties to accommodate for the label. This of course would not be the case for more complex user controls, but it seems OK in your case.
The downside to this though is that you'll have to take the default control template for TextBox and work with it to add your label, which may be a bit trickier.
Either way, I recommend having a look at the Control Authoring Overview page on MSDN, which is extremely useful when writing your first controls in WPF.

How to handle WPF event in MVVM for nested controls in a Window

FINAL NOTE
Final solution found in another post
Although I appreciated the clarification that was provided, the ultimate solution was in-fact provided by another solution as linked above. No matter WHAT I tried, the binding via the "Element Name" component was not working. I had to go based on the "Relative" hierarchy up the datagrid...
<Button Name="btnPrintReport"
Command="{Binding DataContext.MyPrintCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Height="16" Width="16" HorizontalAlignment="Center" >
<Image Source="MyButtonImage.png" IsHitTestVisible="True"/>
</Button>
Hope something not too complicated in WPF / MVVM environment. Here's the scenario.
I have a Window (.xaml) and a corresponding View Model (.cs). The form displays fine with all the data bindings no problem. (note: this is NOT done via any commercial "framework" )
One of the controls that is in the view window is a custom user control of a datagrid with all pre-defined columns, headings and content to be displayed when the view is shown. This works all no problem even though the control is not directly "defined" in the main window .xaml file, but just dropped on the form as the user control itself (which has its own obvious .cs code-behind).
With the main window's "DataContext" pointing to the View Model, and the user control that has a datagrid
<DataGrid AutoGenerateColumns="False"
Name="dataMyStuff"
ItemsSource="{Binding Path=MyTablePropertyOnViewModel,
NotifyOnSourceUpdated=True,
NotifyOnTargetUpdated=True}" ... />
Now, what I'm looking for. On this data grid, I have a column that has an image in the first column. When I click on this image, I want to print a report specific to the record as represented by this row (it has a PK value I use). So, how do I tell the image "KeyUp" event to go to the View Model event handler since that is where the data is, and some other methods I'll need for preparing the call to the report. The view portion of the grid is for cosmetic display to the user, and thus no "functionality" directly in this control.
-- EDIT -- per progress from answers
I've adjusted my datagrid per comments from Josh and Rachel, however, something still does not appear to be quite right... Seeing the button was using a "Command" instance, I interpreted this as it needed to attach to an instance of an "ICommand" interface object on my view model. So, I created an instance. I know the command handler works as it is also used for common things like Add, Edit, Save, Cancel, Exit, etc... So I have a new one for this printing purpose. For simplicity, I have it created as an Always Execute, so there is no method to handle the "CanExecute" portion of the control. I've set the button's "Command" to almost all iterations I could think of an still nothing, but here's an update of what I'm seeing going on.
<UserControl>
<Data grid columns / template, etc to the button>
<DataTemplate>
<Button Name="btnPrintReport"
Command="{Binding DataContext.MyPrintCommand}" >
<Image Source="myPrintImage.png"/>
</Button>
</DataTemplate>
</Data grid columns, etc>
</UserControl>
In my ViewModel class (myICommandButtonHandler inherits from ICommand)
private myICommandButtonHandler myPrintCommand;
public myICommandButtonHandler MyPrintCommand
{
get { if (myPrintCommand == null)
myPrintCommand = new myICommandButtonHandler(myPrint);
return myPrintCommand;
}
}
private void myPrint()
{
MessageBox.Show( "Doing the print job..." );
}
Now, what I'm seeing. During step through initialization of all the controls and such. I click menu item to call my Window to be displayed. FIRST, it creates an instance of the View Model controller. THEN, it calls the Window and passes in the View Model controller as parameter so it is immediately set at the Window level as the "DataContext" of the window. The main window then goes into it's "InitializeComponents" call and starts to build out all the other controls embedded, including this separate class that contains the data grid in question. At the constructor of this usercontrol (that has the datagrid), there is no "data context" set yet as the rest of the controls have not been initialized yet, and I don't know why / when the "bindings" apparently get "hooked" in. So, it appears that trying to do the binding to the data grid's command button are failing. HOWEVER, at run-time, the actual data IS updated in the grid, so I know that much is working.
So, the data grid has its "ItemsSource" set to a property of a "DataView" property on the view model, but the binding of the "button" doesn't appear to see the "MyPrintCommand" handler that I thought would get the hit.. and its action is to just display a message box (for now).
Usually I use an AttachedCommand Behavior which allows me to bind Events to ViewModel Commands. For example, you could use
<Image ...
local:CommandBehavior.Event="KeyUp"
local:CommandBehavior.Command="{Binding DataContext.PrintCommand, ElementName=dataMyStuff}"
local:CommandBehavior.CommandParameter="{Binding }"/>
I'd would recommend using a different event than KeyUp, since I don't think Images can have Keyboard focus so the KeyUp event will never get fired.
A better alternative is to use a Button and overwrite it's Template to be your Image. This will maintain the Click functionality, and give you access to Command and CommandParameter properties
<Button Command="{Binding DataContext.PrintCommand, ElementName=dataMyStuff}"
CommandParameter="{Binding }">
<Button.Template>
<Image ... />
</Button.Template>
</Button>
Also, the CommandParameter="{Binding }" will simply pass the current DataRow's DataContext (your data object) to the command
Change the data template to be a button that has a image as its content. Use the command and commandparameter properties on the button to call your printing method. You can declare your print command in your viewmodel, and bind to it. Your parameter could be the selected row in your datagrid.

Categories

Resources