AutomationElement for WPF ToggleButton - have ClassName Button - c#

I am using ToggleButtons in the ToolBar and I want to get and use them in the UI Automation tests, but when I check AutomationElement.Current for these buttons, its ClassName property is Button, while I would expect ToggleButton
The xaml is not starightforward, so I mention it here :
<ToolBar ItemsSource="{Binding}"/>
for the type that's in the ItemsSource I have a DataTemplate:
<DataTemplate DataType="{x:Type myViewModelType}">
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource MyToolBarElementTemplate}">
<ContentPresenter.Resources>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource ThisStyleSetsWidthAndHeight}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</DataTemplate>
the style is defined as follows:
<Style TargetType="{x:Type ButtonBase}" x:Key="ThisStyleSetsWidthAndHeight">
<Setter Property="styles:AttachedProperties.ContentWidth" Value="32"/>
<Setter Property="styles:AttachedProperties.ContentHeight" Value="32"/>
</Style>
and the content template looks like this:
<DataTemplate x:Key="MyToolBarElementTemplate" DataType="{x:Type myViewModelType}">
<ToggleButton x:Name="AutomationIdThatIGetOk">
...
</ToggleButton>
</DataTemplate>
I am a bit new to Automation Framework, I guess it has to do with all these templates and styles, but is there any way to get the proper AutomationPeer instance created for this ToggleButton?

...but when I check AutomationElement.Current for these buttons, its ClassName property is Button, while I would expect ToggleButton
Your expectation is wrong because the ToggleButtonAutomationPeer class actually returns the string "Button" from its GetClassNameCore() method: https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Automation/Peers/ToggleButtonAutomationPeer.cs,a58abe77888c16cd
So you are getting the proper instance.

Related

Window Style Resource Does Not Override UserControl Property When Present

Given I have a control from somewhere called SomeControl
In MyUserControl.xaml I use SomeControl like so:
<Grid.Resources>
<Window.Resources>
<Style TargetType="local:SomeControl">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="FOO"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
</Grid.Resources>
<Grid>
<!-- Others controls in here -->
<local:SomeControl />
</Grid>
In Window.xaml:
<Window.Resources>
<Style TargetType="local:SomeControl">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="BAR"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:MyUserControl />
</Grid>
The result is it will display Foo when I want it to display Bar.
If I delete the ToolTip from the UserControl, the Window's style is used and it displays Bar like I expect.
Why is the Window style not overriding the UserControl explicit ToolTip property when present, but does when removed?
For the record, I've also tried changing MyUserControl to use a DynamicResource of the same x:Key names and had no affect.
Changing both to ToolTipService.ToolTip also had the same results.
EDIT: I fixed the example to demonstrate that even if the ToolTip is not set on a local level, it still doesn't override the style.
Unless someone can answer how to do override the children's style for the tooltip, I ended up making ToolTip object a dependency property on MyUserControl and passing it down to SomeControl.
The dependency property I made is called ExampleToolTip
MyUserControl.xaml:
<UserControl x:Name='MainControl'>
<UserControl.Resources>
<ToolTip x:Key="DefaultSomeControlToolTip">
<TextBlock Text="FOO"/>
<ToolTip>
</UserControl.Resources>
<Grid>
<local:SomeControl ToolTip="{Binding ExampleToolTip, ElementName=MainControl, TargetNullValue={StaticResource DefaultSomeControlToolTip}}"/>
</Grid>
</UserControl>
Window.xaml:
<Grid>
<local:MyUserControl>
<local:MyUserControl.ExampleToolTip>
<ToolTip>
<TextBlock Text="BAR"/>
</ToolTip>
</local:MyUserControl.ExampleToolTip>
</local:MyUserControl>
</Grid>
Now I can use MyUserControl with it's special "FOO" tooltip on SomeControl and Window has its "BAR" tooltip overriding it.

Multi-stage binding from ViewModel to Custom-Control to controls on ControlTemplate not working

I have a MVVM project with a View and a ViewModel in its DataContext.
In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.
To this ComboBoxCustom class I assign a control template to define its appearance.
The (simplified) style defining the (simplified) control template looks like:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem="{TemplateBinding SelectedItem}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.
I use this ComboBoxCustom control in my View using:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>
The view is ok, and all items get loaded in the ComboBox which I can select.
The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.
When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.
Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.
Thank you!
Thanx to ASh's comment and information in this post.
The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.
The solution is to specify a normal binding as:
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Whichs does about the same as a TemplateBinding but is two way.
The control template has become:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.
The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject}"/>
Bind SelectedValue to the property in ViewModel
SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"
in your lib:ComboBoxCustom

No Style Inheritance Inside ItemsControl / DataTemplate in WPF?

can anyone explain why the TextBlock inside my DataTemplate does not apply the style defined in my UserControl.Resources element, but the second TextBlock ('Test B') does?
I think it may have to do with a dependency property somewhere set to not inherit, but I can't be sure.
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Padding" Value="8 2" />
</Style>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Padding does not apply-->
<TextBlock>Test A</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--Padding applies-->
<TextBlock>Test B</TextBlock>
</StackPanel>
Templates are considered as a boundary. Elements within the templates falls in this boundary range, and look up for the style with a matching target type ends within this range at runtime as a result the TextBlock outside will pickup the style and the one inside wont. like adminSoftDK said you should give the style an x:Key and then apply it as static resource it will work.

ContentControl set type of displayed control from ViewModel

I got custom wizard control, that used in different projects.
As example page4 used in one project, and another project use only page 1 and 2.
Is there any way to make viewModel provide type of control or something like that, to make contentcontrol use generic pages and show proper controls?
To make it clear, i dont want hardcode controls related to different projects, but decide witch control to show dynamically.
...
<ContentControl Grid.Row="1" Grid.Column="1" Content="{Binding CurrentPage}">
<ContentControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModelControls:WizardPage1ViewModel}">
<viewModelControls:WizardPage1Control/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModelControls:WizardPage2ViewModel}">
<viewModelControls:WizardPage2Control/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModelControls:WizardPage3ViewModel}">
<viewModelControls:WizardPage3Control/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModelControls:WizardPage4ViewModel}">
<viewModelControls:WizardPage4Control/>
</DataTemplate>
...
</ResourceDictionary>
</ContentControl.Resources>
</ContentControl>
...
If I understand you the right way, you want to have one Window,
which hosts different controls while being in a specifc state?
So if, for Example, a button Login is pressed, you want to switch to another window with other controls?
I would make DataTemplates for the controls like you did:
<Window.Resources>
<DataTemplate x:Key="ShowLoginView">
<local:LoginView />
</DataTemplate>
<DataTemplate x:Key="ShowEditView">
<local:EditView />
</DataTemplate>
</Window.Resources>
And set a trigger to the contentcontrol:
<Style x:Key="ContentControlStyle" TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static Application.Current}, Path=SessionState}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource ShowLoginView}"/>
</DataTrigger/>
<DataTrigger Binding="{Binding Source={x:Static Application.Current}, Path=SessionState}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource ShowEditView}"/>
<DataTrigger>
</Style.Triggers>
</Style>
Then simply add the style to the contentcontrol:
SessionState has to be a shared resource (in App.xaml).
Now you can change the Window/UserControl by simply calling:
if(Application.Current != null && Application.Current is App)
{
(Application.Current as App).SessionState = 0; // Login-Control
//or
(Application.Current as App).SessionState = 1; // Edit-Control
}
Note:
Don't forget to add the NotifyPropertyChanged to SessionState, otherwise the binding won't work.

Setting a style on Control type not working

I'm writing a very basic WPF dialog and want to apply a simple style to all objects that inherit from the Control class. The code I'm using:
<Window.Resources>
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="some text"/>
<TextBox x:Name="x_NameTextBox"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="x_CancelButton" Click="x_CancelButton_Click" Content="Cancel"/>
<Button x:Name="x_OkButton" Click="x_OkButton_Click" Content="OK"/>
</StackPanel>
</StackPanel>
</Window>
The Style defined above doesn't change the layout of the window at all unless I specify a key and set the style on each individual object, which is exactly what I'm trying to avoid. It also works for more specific types (setting the TargetType to Button, for example.)
Any ideas why this isn't working?
Every control when it gets instantiated it gets its Style from the explicitly defined resource or look for the immediate parent where it can get a default style. In your case the Button control will get its default Style from the platform because your App haven't defined one. Now that platform Button Style has no way to know about your custom defined Control base style. Because styles will look for a base style only when you explicitly define BasedOn
So you got only two ways
1. Define Style for every control - which you don't want I think.
2. Define Styles for the controls you are interested and set the BasedOn
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}">
</Style>

Categories