I'm developing a Silverlight app and would like to create a grouping of 5 toggle buttons (used for menu options) that animate when clicked (grow in size) and also cause any previously clicked buttons in the group to unclick and animate back to their shrunken size.
I know I could use a brute force approach where the app is directly aware of each button, but if I add or change the menu (add/remove a button) I'd have to remember to modify the code (which is bad since I'm forgetful). Is there a way to more intelligently group the buttons so that when one is clicked is can tell all the others in the group to unclick?
Thanks!
Todd
Special shout-out to Michael S. Scherotter for pointing me in the right direction to use RadioButton and a control template!
Here's the basic control template that I came up with. Put this in the App.xaml between the tags if you care to see it. The UX folks will give it a once over to pretty it up, but for now, it works as a radio button that looks like a toggle button (or just a button), but has a groupname.
An important note: this template doesn't have any of the basic button animation, so it won't press down when clicked... That's the UX work I mentioned above.
<Style x:Key="MenuButton" TargetType="RadioButton">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border BorderBrush="DarkGray" BorderThickness="3" CornerRadius="3" Background="Gray">
<!-- The ContentPresenter tags are used to insert on the button face for reuse -->
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I was trying to do the same thing and I found this old post. Let me update it a bit...
Todd seems to be like many other lazy programmers, letting the UX guys do all the work. :) So here is Todd's solution with actual toggle buttons as radio buttons in a group:
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="One"
IsChecked="True" />
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="Two" />
<RadioButton GroupName="myGroup"
Style="{StaticResource MenuButton}"
Content="Three" />
<Style x:Key="MenuButton" TargetType="RadioButton">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border BorderBrush="DarkGray" BorderThickness="3" CornerRadius="3" Background="Gray">
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
<ToggleButton.Content>
<ContentPresenter></ContentPresenter>
</ToggleButton.Content>
</ToggleButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And now you'll have your pretty button rollover effects and all that and only one toggle button will be able to be 'checked' at one time.
Both radio buttons and toggle buttons can be three state, and you can bind the 'IsThreeState' property of the toggle button as well in the same manner that I bind 'IsChecked' here. But by default they are two state.
Also, the long form binding is important here as the shortcut {TemplateBinding IsChecked} would default to one way and we need them to stay in sync both ways.
This example does not, of course, animate the buttons with size changing and all that Todd originally posted, but it does give you regular buttons that you can easily distinguish as being checked or not.
Give all of the RadioButton objects the same GroupName.
Related
I am using wpf style on my controls so that i can use an style on multiple controls at once. It usually works. for example i made an saveButtonStyle and i apply it on every save button on my application. But it doesn't work on MenuItems.
I made a style for my menuitems which contains an icon next to the items.
This is one screen shot of it.
You see the Datagrid has an ContextMenu and within it there is multiple menu items. in this case pay attention to Set Alarm. it has an icon. This Set Alarm Menu item is also in another menu datagrid next to this one. When i click that one
it appears too
But problem is when i right click back to the other datagrid the icon is gone and wont come back. this is the screen shot
Here is the style I made
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon">
<Setter.Value>
<Image Source="Content/AlertIcon.png" Width="20" Height="20" />
</Setter.Value>
</Setter>
</Style>
And here is how I apply it to my controls
<MenuItem x:Name="customerContextMenuSetAlarm" Header="SetAlarm" Style="{StaticResource menuItemAlert}" Click="customerContextMenuSetAlarm_Click"/>
Do you know why it happens?
style menuItemAlert creates only one instance of Image and can display it in one place only. to overcome this make a separate non-shared resource for that Image.
<Image x:Key="AlertIcon" x:Shared="False" Source="Content/AlertIcon.png" Width="20" Height="20" />
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource AlertIcon}"/>
</Style>
I have 2 buttons, both of which are using a style to keep a consistent UI. However for 1 button, I would like to provide an image for, and the other just text.
I suppose I could create a new style, copy everything over, and the reformat it to my liking, but this seems like waste, is time consuming, and i think i would have to do it for each instance I wish to have a image on a button. I mean, that's fine, but I just want to know if there is an alternative that would make things more elegant.
I think I should somehow be able to push an 'arguement' or data to a style, either in or out of XAML to format the style, or something that would accomplish this (I'm sure the terminology is wrong).
Here is the Button Style:
<Style x:Key="Control_Button" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image> <!-- Optional Image here --> </Image>
<TextBlock Name="btn" Text="{TemplateBinding Content}" Padding="16" VerticalAlignment="Center">
<TextBlock.TextDecorations>
<TextDecoration Location="Underline" />
</TextBlock.TextDecorations>
</TextBlock>
<!-- FIX ME: not underlined normally -->
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TextBlock.IsMouseOver" Value="True">
<Setter TargetName="btn" Property="TextDecorations" Value="none" />
<!-- FIX ME: underlined on hover -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What you are asking for is not possible without creating a custom control or user control.
What you should do though is setting the contents of the button the way you like. If you want only a string, you can set it directly:
<Button>my text</Button>
or with a binding:
<Button Content={Binding textProperty} />
To include an image in the button, add a panel as content, in this example I added a StackPanel, but you can also use a Grid or any other element:
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding myImagePath}" />
<TextBlock Text="{Binding myText}" />
</StackPanel>
</Button.Content>
</Button>
Hi all this is a problem that has been driving me crazy for a few days now.
Put simply whenever i declare a foreground colour on anything that derives from a TextBlock control that foreground colour is recognised at design time but at run time it always defaults to being black.
Its as if the foreground property is being ignored on the control.
So for example normally i would expect the following to render a button with white text:
<Button x:Name="MyButton" Content="Hello World" Foreground="White" ... />
However this renders a button and the foreground text colour is black. Its effectively ignoring the Foreground setter property.
The only way to get this to work as expected is to the do following:
<Button x:Name="MyButton" .... >
<TextBlock Text="Hello World" Foreground="White"/>
</Button>
This way works and the button renders correctly with white text. But i know i shouldnt have to explicitly define the buttons textblock like this.
The same behaviour occurs with anything that derives from textblock.
Does anyone have any idea why this is happening ?
UPDATE:
I have checked my solution for styles that are applied to TextBox. I have defined my own style on the TextBlock which is:
<Style x:Key="TextBlockText" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#FF63798F"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
Which as you can see defines a value for the foreground. However when i remove this style from my resource dictionary the above problem still remains.
Additional information is that I am using the MahApps.Metro libraries and Im wondering if this is causing the issue.
Does anyone have any other ideas ? Or even thoughts of where to investigate ??
Every control in WPF has a Template associated with it. I think somehow a style defined on your button which does not count the foreground property.
For instance,
<Style x:Key="DialogButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding BorderBrush}"/>
<TextBlock Foreground="{TemplateBinding Foreground}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/></TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Can you use Style={StaticResource DialogButtonStyle} and define foreground for the Button. See here we used TemplateBinding on Foreground of the TextBlock rather than defining the color inside it.
I'd recommend keeping your TextBlock style as it is, and in your Button or other controls, at the Template level, add a new Resource with a TextBlock style. It will be in that template's domain so it won't affect the other textblocks but will override the style of the main TextBlock.
For example:
<ControlTemplate>
<Grid>
<Grid.Resources>
<!-- Put a new/duplicate TextBlock Style here with
the appropriate Foreground color, or TemplateBinding
and it will override it for this Grid's children -->
</Grid.Resources>
<TextBlock />
</Grid>
</ControlTemplate>
This question already has answers here:
WPF - How can I place a usercontrol over an AdornedElementPlaceholder?
(2 answers)
Closed 2 years ago.
I have a view defined as a DataTemplate (for an "OrderEntryViewModel") with a Menu, ContentPresenter, and Expander inside of a 3-row grid. The content of the ContentPresenter is binded to another viewModel "OrderViewModel" (for which there's another DataTemplate-defined view). The expander has a ZIndex of 99, so that when it expands UP, it expands OVER any other controls (i.e. the ContentPresenter).
This all works as expected EXCEPT when the ContentPresenter's content (the OrderViewModel) has data errors...My OrderView displays a custom validation error template around controls with invalid data. What happens is, when I expand the expander, all of controls inside the ContentPresenter are covered, but the red border and exclamation point I show are still visible through the expander! I've verified that my expander's ZIndex is 99 and the ContentPresenter's is 0. Can anyone help me with this?
Here's some images to help explain:
First Image shows what the views look like when NOT expanded.
Second Image shows what the views look like when I expand.
I define the validation error template like this:
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel LastChildFill="true">
<Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="10" Height="10" CornerRadius="5"
ToolTip="{Binding AdornedElement.(Validation.Errors).CurrentItem.ErrorContent, ElementName=customAdorner}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="White"/>
</Border>
<AdornedElementPlaceholder x:Name="customAdorner" VerticalAlignment="Center">
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
And assign it to a specific control like this (this is how I do it for my TextBox):
<Style TargetType="{x:Type TextBox}" x:Key="ValidatedStyleTextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLocked}" Value="True">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsLocked}" Value="False">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationErrorTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
This solution worked for me...just added an AdornerDecorator at the same level as my expander, so now, the controls inside of the AdornerDecorator use that layer to display instead of the top level layer in the window
I've been asked to create a new control for a feature that we need, I'm really struggling on how to best achieve this in a generic and flexible way.
basically the control is very similar to a tree view, where as you would click on a node and then its children would then appear underneath that node.
for this control, they want it like this - when you click on a node, its children are then displayed on the left of parents container and so on.
I've done a quick (super quick) diagram in paint... (please don't laugh, its terrible! :) )
So you should just start with a single list of items and then progressive through the children of the selected item..
so my question is really, where do you start on something like this.. ? I'm fine with the data side but the XAML is the bit that's really confusing me, it needs to be very generic so it could potential cope with 100's of children panels
any help would be brilliant.
cheers.
ste.
If you are after a usercontrol and known bindings at designtime - it would be easy, but as a custom control - i'm very interested in good answers
like i said this can easy be done if you know the collection bindings and the childlevel. but its maybe a start to get a custom control.
<UserControl>
<UserControl.Resources>
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem">
<Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type local:TreeItems}">
<Grid VerticalAlignment="Stretch">
<Border BorderBrush="Black" BorderThickness="1" >
<TextBlock Margin="5" Text="{Binding Name}"/>
</Border>
</Grid>
</DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<ListBox x:Name="root" ItemsSource="{Binding}"></ListBox>
<ListBox x:Name="Lvl1" ItemsSource="{Binding ElementName=root, Path=SelectedItem.Childs}" />
<ListBox x:Name="Lvl2" ItemsSource="{Binding ElementName=Lvl1, Path=SelectedItem.Childs}" />
</StackPanel>
</UserControl>
This article from Josh Smith is a good starting point.
If all you need is a hierarchical data control, you better use a TreeView and not rolling your own control, as it is probably more complicated than you think (selection, keyboard navigation, etc.)