As the title says, I want to hide or display some content dynamically. I have two buttons working like radio buttons. Each button has to display a specific content, but the two contents must not be visible at the same time (it's a kind of form, and I'm working on two subclasses).
I've seen on SO two interesting things, and I wanted to mix them. Firstly, the use of ContentControl with a ToggleButton (http://stackoverflow.com/questions/7698560/how-to-bind-click-of-a-button-to-change-the-content-of-a-panel-grid-using-xaml). Secondly, using ToggleButtons as RadioButtons (http://stackoverflow.com/questions/2362641/how-to-get-a-group-of-toggle-buttons-to-act-like-radio-buttons-in-wpf second answer with 31)
So I decided to start with something like that :
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<WrapPanel HorizontalAlignment="Center">
<TextBlock Text="{x:Static Internationalization:Resources.MAINOPTIONSWINDOW_STATUSLINKSUPERVISOR}"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Alarm" GroupName="Trigger"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Event" GroupName="Trigger"/>
</WrapPanel>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
But Visual Studio underlines
{StaticResource {x:Type ToggleButton}}
Visually, my buttons appear like radio buttons instead of toggle buttons.
Visual says :
Impossible to resolve the resource "{StaticResource {x:Type
ToggleButton}}" (translated from french ;) )
Anyway this code works fine outside the ContentControl.
Yeah, first of all, fix your eventHandlers. Dont attach them on templates, tricky things can happen.
Second of all, you can modify your ContentControl.Resources, and put this to there:
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="RadioButton"/>
And remove your own style definitions. You can even put that one level more up(parent of ContentControl). One more thing, you're doing this in code-behind, but I can assure you, true power of WPF comes from the fact that you dont need code-behind, atleast for logic like this.
Also the link you gave, it is using triggers. This is no biggie right now but you should know that it's not correct way. If you wanna switch between contents, check out ContentControl Content, DataTemplates, DataTemplateSelector. The idea is to have only one VIEW at memory, not hide things.
That can catch up, if youre doing the other way. It will eventually add more time to startup and memory usage.
In control templates you normally do not add event handlers.
Override the OnApplyTemplate and add the handler there.
I finally found another way
<RadioButton Content="Alarm" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToAlarmCommand}"/>
<RadioButton Content="Event" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToEventCommand}"/>
<ContentControl Content="{Binding Path=Trigger, ElementName=Window}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type States:AlarmTrigger}">
<ComboBox ItemsSource="{Binding Path=AlarmTriggersCollection}" />
</DataTemplate>
<DataTemplate DataType="{x:Type States:EventTrigger}">
<ComboBox ItemsSource="{Binding Path=EventTriggersCollection}" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
With code behind changing type of Trigger
Related
I'm creating a custom TextBox with a Title and I'm trying do it with a template.
The problem is that I don't want to have a textbox inside of textbox.
For example:
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<StackPanel Height="30" VerticalAlignment="Top" Background="green">
<TextBlock Text="Title"/>
<TextBox Text="{Binding Text, ElementName=txtBox}"/> <!-- How I can do it without this? -->
</StackPanel>
</ControlTemplate>
</TextBox.Template>
This way works like I want but I don't want to recreate the TextBox with binding.
Or is this the right way?
You're not putting "a textbox inside a textbox", you're putting it inside a template. Very different thing. There's still only one textbox, you're just extending its appearance.
If you want fundamental TextBox behavior then you have to use a TextBox in your template, there's not really any way around that. I suspect though that the real problem here isn't so much the TextBox as it is the explicit binding. If that's what you're trying to avoid then you can use a TemplateBinding to bind to the templated control's Text property instead:
<TextBox Text="{TemplateBinding Text}"/>
EDIT: just as a follow-up to this, TemplateBinding is one-way only. If you need two-way binding then use Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" instead.
I am writing a folder browser and I want to open folders on double click.
My folders are binded to ListView with GridView inside and I am tracking double click like this:
<i:EventTrigger EventName="MouseDoubleClick">
<Custom:EventToCommand Command="{Binding FolderOpenedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=FolderView}"/>
</i:EventTrigger>
But I have an annoying issue: if I double click on gridview splitter to autosize column, it will also open selected folder, which I am not want to.
So, I have several options for now: put event handler inside style and use it with code behind or leave it as is, but in my case I want to do implement it with MVVM scenario because codebehind is not suitable for me.
My question is: how I can send my parameter as SelectedItem only if I click on the item and null when I click on something else?
I want to track this to make a proper behavior as far as I cannot apply double click to gridview on some reason.
Could someone please help me with this problem?
EDIT:
Lets clarify one thing to be sure we speak about the same things:
I can define something like this
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="FolderView_OnMouseDoubleClick"></EventSetter>
</Style>
Bu I cannot do like this:
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="{Binding OpenFilesCommand}"></EventSetter>
</Style>
Because It will lead to exception. Now I want to understand how I can apply command here if Handler does not accept command? Do I need to write some attached property?
Avoid using listview. Use DataGrid instead. Then you can add eventtrigger to row style. ListView is obsolete class introduced in wpf 3 that was replaced by datagrid in wpf 4 an there no reason to use it anymore.
Another option is to use use custom behaviour implemented as attached property, e.g. InvokeCommandOnRowDoubleClick attached to Grid. To learn more about attached behaviours read this: http://blogs.msdn.com/b/dgartner/archive/2009/11/11/wpf-attached-behavior-example-watermark-text.aspx
There is a MouseDoubleClick property on the ListViewItem control. You can rework your style to contain a correct event when double clicking an item, apply it only to the ListViewItems and it won't listen to that event when not double clicking the gridview splitter.
You can read more about this here.
Well, to solve this issue I had to use InputBindings property for each control in each gridview column. I put Grid over controls and made like this:
<GridViewColumn Header="Size (Bytes)">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.FolderOpenedCommand}"
CommandParameter="{Binding ElementName=FolderView, Path=SelectedItem}"></MouseBinding>
</Grid.InputBindings>
<TextBlock Text="{Binding Path=Size, StringFormat='{}{0:#,#.}'}"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Unfortunately for my case I didnt find better solution
I have successfully implement a WPF menu where the top-level items are drawn as large buttons and the lower level items are drawn as standard menu items (see my previous questions here and here).
In my original attempt at this my lower-level item template (SubItemTemplate in the example below) contained an image and a textblock. The result was something that looked like a normal menu item with an empty Icon area and the image next to the text in the text part of the menu item. I was not expecting to see the icon area in the visual display since I thought that the entire visual display would be determined by the contents of my template. The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
When I removed my image from teh lower-level template and replaced it with a style-setter for the Icon property, I got the display that I wanted.
I do not understand how and why the Icon property exists on my lower-level item DataTemplate.
Here's my code. The property HasParent is used to distinguish menu items that are not top-level (that is, the ones that are drawn with the SubItemTemplate). The section I don't understand is the DataTrigger.
Why is there an Icon property available inside that trigger?
<UserControl.Resources>
<Image x:Key="MenuIconResource16" Height="16" Width="16" Source="{Binding Icon32}" x:Shared="False" />
<HierarchicalDataTemplate x:Key="TopLevelItemTemplate" ItemsSource="{Binding Children}">
<StackPanel VerticalAlignment="Bottom" Orientation="Vertical">
<Image Width="32" Height="32" VerticalAlignment="Center" Source="{Binding Icon32}" ToolTip="{Binding UserHint}" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="SubItemTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<WrapPanel Height="Auto">
<Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}" ItemTemplateSelector="{StaticResource MenuItemTemplateSelector}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding EventType}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasParent}" Value="true">
<Setter Property="Icon" Value="{StaticResource MenuIconResource16}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
</Menu>
</WrapPanel>
I thought that the entire visual display would be determined by the contents of my template.
#dkozl noted the difference between DataTemplate and Template -- that is the important distinction. A data template is a XAML fragment that the owning control uses as part of the overall control, which may or may not include other (customizable or hard-coded) visual elements, and/or other data templates. The control template is where this visual structure of the control is defined. If you set/override a control template, then your expectation of not seeing any other visual content, will hold true.
The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
The other thing to note here is that the default style for Menu defines multiple control templates for its MenuItems. These templates are applied depending on the role "TopLevelHeader", "TopLevelItem", "SubmenuHeader", and "SubmenuItem". So you will see different behavior for these different menu items. Take a look at the default styles/templates, which should be illuminating (although they are kind of complex).
Why is there an Icon property available inside that trigger?
A style trigger has the capability of modifying any dependency property of the control it is applied to. Since the style trigger in question is being applied to the MenuItem control, it can modify the Icon dependency property, which is owned by that control.
Working to create a top level menu in WPF, I used the trick of styling a RadioButton as a ToggleButton in order to get the "only one selected" effect. Like this:
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Key.Name}" GroupName="MenuButtonGroup"
Style="{StaticResource {x:Type ToggleButton}}" >
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It works beautifully, and does just what I expect. But Visual Studio registers it as an error.
The Style property is underlined in blue and the description given is The resource "{x:Type ToggleButton}" could not be resolved.
It all looks above board but having this sat on my error list in Visual Stuido is hugely irritating. Any idea how to resolve it?
EDIT: Just found this question -
The resource could not be resolved (VS 2010 RC)
Which suggests it's a VS error. Anyone confirm this, or know of a fix? Wherever the problem is, it's still really annoying!
You could move the style to a Resources collection e.g.
<Window.Resources>
<Style x:Key="MyStyle" TargetType="{x:Type RadioButton}" BasedOn="{StaticResource {x:Type ToggleButton}}" />
</Window.Resources>
and then reference that style:
<RadioButton Content="{Binding Key.Name}" GroupName="MenuButtonGroup"
Style="{StaticResource MyStyle}" />
I am modeling an attached command pattern after the AttachedCommandBehavior library here. My button looks like this:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="vms:Attached.Behaviors">
<Setter.Value>
<vms:Behaviors>
<vms:Behavior Event="Click"
Command="{Binding ClickCommand}" />
</vms:Behaviors>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Everything works great, but when the setter on the Behavior is executed, the Command is null.
Behavior is a Freezable, and Behaviors is a FreezableCollection<Behavior>. It just doesn't seem to be inheriting the DataContext from the Button.
On the other hand, this works correctly:
<Button>
<vms:Attached.Behaviors>
<vms:Behavior Event="Click" Command="{Binding ClickCommand}" />
</vms:Attached.Behaviors>
</Button>
Unfortunately I can't do it this way, because I need to target generated ListViewItems using ItemContainerStyle.
Is there some way to get the DataContext in the Style?
The Attached Command Behavior library is the germ of the idea that became Blend Behaviors. The Blend Behaviors are much more powerful and standardized and so I recommend you switch to using them. But whether you are using Attached Command Behavior or Blend Behaviors, the problem is essential the same: they don't work as expected when trying to set them using a style. I've solved this problem for Blend Behaviors with full support for binding in this StackOverflow answer:
How to add a Blend Behavior in a Style Setter
Without testing it, I guess you have to move the ACB behavior to a resource marked with x:Shared="False" in order to get the binding to work.
I had the same problem, and using RelativeSource did the trick. I'll show you my before and after code...
Before: (This DIDN'T work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding OpenMenuItem}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
After: (This DOES work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding Path=DataContext.OpenMenuItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
You'll obviously have to tweak the parameters of the Relative Source to your specific situation. It seems that, for whatever reason, attached properties don't inherit the data context, so you have to tell if how to.