I'm having a really hard time finding a way to do the following...
I have my "Image button style," and it is applied to every button in my Silverlight app. Is there any way to make a blanket VisualStateManager to change the image on MouseOver, that will work for every button? I have a couple ideas, but I'm not sure if they can be implemented...
Let's say my button image paths are consistent:
image1.png
image2.png
and
image1 - hover.png
image2 - hover.png
Is there a way to change the path of the image to append " - hover"? I thought about using an IValueConverter for this, but wasn't sure how to access the control state...
An alternative idea I had was if there was a way to accomplish this:
<Button>
<Image Source="../Images/image1.png" Width="70"/>
<Image Source="../Images/image1 - hover.png" Width="70"/>
</Button>
Have two contents (an array of content?) and on Normal state, set only the first content opacity to 1, and the rest to 0. Then on MouseOver, switch the opacities.
Are any of these solutions feasible, and how can they be implemented?
Thanks
Edit: I know I can make custom styles for each button that switch out their images, but I would like a blanket style that I can apply to all buttons.
You may (ab)use the Tag property for your purpose and create a ControlTemplate with two Image controls with a BitmapImage that binds its UriSource property to either the Content or the Tag property respectively:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="image1"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.1"/>
<DoubleAnimation
Storyboard.TargetName="image2"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image x:Name="image1">
<Image.Source>
<BitmapImage UriSource="{Binding Content,
RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Image.Source>
</Image>
<Image x:Name="image2" Opacity="0">
<Image.Source>
<BitmapImage UriSource="{Binding Tag,
RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Image.Source>
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now you may declare a Button like this:
<Button Content="../Images/image1.png" Tag="../Images/image1_hover.png"/>
Related
I am trying to use visual states to mark a TextBox input as invalid (by changing its border color to red) during input validation when a user submits a form the TextBox is a part of. I have the following code:
XAML
<Page.Resources>
<!-- Other resources omitted for brevity -->
<Flyout x:Key="NewTimeBlockFlyout">
<StackPanel>
<!-- Other stuff here omitted for brevity -->
<TextBox Margin="5"
Header="Name"
x:Name="NewTimeBlockNameTextBox">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Default"></VisualState>
<VisualState x:Name="Invalid">
<VisualState.Setters>
<Setter Target="NewTimeBlockNameTextBox.Background" Value="Red"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</TextBox>
<!-- The rest of the form omitted for brevity -->
<Button x:Name="CreateTimeBlockButton"
Grid.Column="0"
Margin="0,0,2,0"
HorizontalAlignment="Stretch"
Tapped="CreateTimeBlockButton_Tapped">Create</Button>
</StackPanel>
</Flyout>
</Page.Resources>
Note: This is on a XAML Page. I am NOT using a custom control.
C#
private void CreateTimeBlockButton_Tapped(object sender, TappedRoutedEventArgs e)
{
// Validate the input.
if (String.IsNullOrWhiteSpace(this.NewTimeBlockNameTextBox.Text))
{
// These two lines of code confirm the visual state named "Invalid" does exist on the textbox.
//List<VisualStateGroup> m = VisualStateManager.GetVisualStateGroups(this.NewTimeBlockNameTextBox).ToList();
//List<VisualState> c = m.FirstOrDefault().States.ToList();
// Assignment to bool just used to inspect the return value for debugging.
bool did = VisualStateManager.GoToState(this.NewTimeBlockNameTextBox, "Invalid", false);
}
}
Problem
No matter what I try, the call to VisualStateManager.GoToState() is always returning false.
Things I have tried:
Here is the relevant documentation from Microsoft.
As seen in the C# code above, I have verified the visual state "Invalid" does exist, as expected, on the "NewTimeBlockNameTextBox" control.
I have seen several solutions including here, and here that suggest moving the <VisualStateManager.VisualStateGroups> tag in the XAML to outside of the TextBox, or to the root of the Page. Neither have worked for me.
I have also seen these two solutions here and here, but neither seemed relevant to my situation, as both seem to have issues related to things I am not doing.
I can reproduce your issue. You need to place <VisualStateManager.VisualStateGroups> outside the TextBox and skip status through VisualStateManager.GoToState(this, "Invalid", false); Please refer to the following code.
Xaml code:
<Page
……
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Default"></VisualState>
<VisualState x:Name="Invalid">
<VisualState.Setters>
<Setter Target="NewTimeBlockNameTextBox.BorderBrush" Value="Red"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBox Margin="5"
Header="Name"
x:Name="NewTimeBlockNameTextBox" >
</TextBox>
<Button x:Name="CreateTimeBlockButton"
Grid.Row="1"
Margin="0,0,2,0"
HorizontalAlignment="Stretch"
Tapped="CreateTimeBlockButton_Tapped">Create</Button>
</StackPanel>
</Page>
Code behind:
private void CreateTimeBlockButton_Tapped(object sender, TappedRoutedEventArgs e)
{
bool a = VisualStateManager.GoToState(this, "Invalid", false);
}
Updated:
For using VisualStateManager.GoToState(), which always be done by changing its controltemplate.
You could right click the textbox in XAML designer, then select the option Edit a Template->Edit a Copy, here you will see the default textbox style placed in <Page.Resources> </Page.Resources> tag.
<Style x:Key="TextBoxStyle1" TargetType="TextBox">
……
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
……
<VisualStateManager.VisualStateGroups>
……
<VisualState x:Name="TestState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="red"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="HeaderContentPresenter" …/>
<Border x:Name="BorderElement" …/>
……
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see, you could place custom state in its template, then you could use “VisualStateManager.GoToState(NewTimeBlockNameTextBox, "TestState", false);” to skip state.
I'm just starting to learn UWP and xaml. What is the proper way to add a AutoSuggestBox to the Side Navigation panel? (Sorry for the bad code formatting in advance, copy and paste wasn't great)
My Main.xaml has an AutoSuggestArea that I have set to Visible
</VisualStateGroup>
<VisualStateGroup x:Name="AutoSuggestGroup">
<VisualState x:Name="AutoSuggestBoxVisible"/>
<VisualState x:Name="AutoSuggestBoxCollapsed">
<VisualState.Setters>
<Setter Target="AutoSuggestArea.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
and in the Grid for the AutoSuggestArea I have defined an AutoSuggestBox
<Grid x:Name="AutoSuggestArea" Height="44" Grid.Row="3" VerticalAlignment="Center">
<ContentControl x:Name="PaneAutoSuggestBoxPresenter" Content="{TemplateBinding AutoSuggestBox}" HorizontalContentAlignment="Stretch" IsTabStop="False" Margin="16,0,16,0" VerticalContentAlignment="Center"/>
<Button x:Name="PaneAutoSuggestButton" Content="" MinHeight="44" Style="{TemplateBinding PaneToggleButtonStyle}" Visibility="Collapsed" Width="{TemplateBinding CompactPaneLength}"/>
<AutoSuggestBox Width="234" VerticalAlignment="Center"
HorizontalAlignment="Center"
PlaceholderText="Search" Name ="boxS"
QuerySubmitted="AutoSuggestBox_QuerySubmitted"
TextChanged="AutoSuggestBox_TextChanged">
<AutoSuggestBox.TextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="IsHandwritingViewEnabled" Value="False"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</AutoSuggestBox.TextBoxStyle>
<AutoSuggestBox.QueryIcon>
<SymbolIcon Symbol="Find" Foreground="Black">
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="1" ScaleY="1"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
</AutoSuggestBox.QueryIcon>
</AutoSuggestBox>
</Grid>
What I want is basically Identical Behaviour as the Groove Music app on Windows, where the Search bar disappears as the Nav View is closed or Minimized.
Instead I get this
I am assuming you meant NavigationView by NavigationPanel.
This is not how you put an AutoSuggestBox in NavigationView. NavigationView has an NavigationView.AutoSuggestBox property. You just set an AutoSuggestBox on this property, and every thing will work as expected. Like this:
<NavigationView>
<NavigationView.AutoSuggestBox>
<AutoSuggestBox x:Name="NavViewSearchBox" QueryIcon="Find"/>
</NavigationView.AutoSuggestBox>
</NavigationVew>
You don't have to hide/show this AutoSuggestBox yourself. NavigationView will automatically do this for you. Also, you don't have to put thie AutoSuggestBox inside any grid or anything.
Im recently working on some simple Imageviewer.
Now it came to my mind, it might be a nice feature, to do some context-sensitve actions like Zooming and rotating.
To implement these functions is not my problem, but the ContextMenu is.
I've decided to not use a ContextMenu-Element, instead im going to use a popup.
Reasons for PopUp:
Less Styling
Better Positioning
IsOpen is Bindable (ContextMenu is NOT bindable on IsOpen against all Articles regarding this)
Here comes the trouble:
<Image x:Name="PART_ImgCurrent" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" Grid.Row="0" Grid.Column="0"
Source="{Binding ElementName=PART_PreviewPanel, Path=SelectedItem.Source}">
<Image.LayoutTransform>
<RotateTransform Angle="0"></RotateTransform>
</Image.LayoutTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ImgCurrent" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
<Popup IsHitTestVisible="False" Focusable="False" PlacementTarget="{Binding ElementName=PART_ImgCurrent}" AllowsTransparency="True" StaysOpen="True"
IsOpen="{Binding ElementName=PART_ImgCurrent, Path=IsMouseOver, Mode=OneWay}"
Placement="Right" HorizontalOffset="-42" VerticalOffset="2">
<StackPanel Opacity="0.5" VerticalAlignment="Stretch">
<Button Content="Ugly Button" Height="40" Width="40"></Button>
<Button Content="Ugly Button" Height="40" Width="40"></Button>
<Button Content="Ugly Button" Height="40" Width="40"></Button>
</StackPanel>
</Popup>
As you can see, im binding IsOpen of Popup to IsMouseOver on Image which results in a funny Disco-BlinkenLights-Behavior when i try to click a button inside the Popup.
What has this to do with the Title?
AcrobatReader has this
This is almost exactly the behavior im looking for. How is this thing called?
Or had someone ever similar issues and could provide a solution?
Sorry for the delay, soon as I thought I had a second I got busy again. Anyway, here's one of several ways I can think of accomplishing your goal, give it a shot. I sort of assumed it may not be just images you want this for and if you threw the resource stuff in a dictionary and kept your naming consistent (or even better, just target the nested UIElement) you could use it all over the place. Notice the Grid is acting as what would be the Image in this example.
I generally make things open for future added interactions and stuff, so in this case I would probably just make the image source the background brush for Grid or place it as a child. That way if you decide to add other objects in there or say other effects and stuff you've got a good start point.
Anyway I digress, so try out the concept example below and see if it's what you're after. If not, like I said there's several other ways I can think of to accomplish your goal so just let me know. :)
<!-- HitTestVisibility Area -->
<Grid x:Name="ImagePlaceholder"
Height="500" Width="500"
Background="LightBlue">
<Grid.Resources>
<Storyboard x:Key="OnMouseEnter">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="FakePopUp">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnMouseLeave">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="FakePopUp">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Grid.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource OnMouseEnter}"/>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource OnMouseLeave}"/>
</EventTrigger>
</Grid.Triggers>
<!-- Overlay -->
<Border Name="FakePopUp" Visibility="Collapsed"
Margin="0,0,0,25" Background="SlateGray"
Height="50" CornerRadius="20" Padding="10"
HorizontalAlignment="Center" VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal">
<Button Content="Eins Bier"/>
<Button Content="Zwei Bier" Margin="10,0"/>
<Button Content="Drei Bier"/>
</StackPanel>
</Border>
</Grid>
I went with Storyboards attached to the parent instead of direct triggers with TargetName like I said, because I could think of a bunch of other instances features might want to be added that would make sense. Even something simple like adding a transition duration for a nice fade effect or maybe a translate y to slide it up while fading etc, etc, etc.
Anyway, hope this helps. Cheers!
I saw some examples for changing colors and brushes for selected item in a ListBox
I was wondering if there is a way to change visual properties of an item in a list box based on events in our code
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="spSceneThumb" Width="110">
<Border BorderThickness="1" Background="#FFfcfcfc" BorderBrush="#aaaaff" >
<StackPanel></StackPanel>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Let say I want to change border color of 5th item based on some event
I tried IValueConverter But changes to property wont effect border color
You can declare a custom RoutedEvent that you could listen to with an EventTrigger. You can find out how to declare a custom RoutedEvent in the How to: Create a Custom Routed Event page on MSDN. Once created, you can reference your custom event using the class name before the event name and not forgetting the XAML Namespace Prefix that you define for the namespace where it was declared. Something like this:
RoutedEvent="YourNamespacePrefix:YourClassName.YourEventName"
However, changing discrete values like Brushes is not so simple using an EventTrigger. You'll have to use a Storyboard with a DiscreteObjectKeyFrame element. You could try something like this:
<DataTemplate x:Key="Something">
<StackPanel Name="spSceneThumb" Width="110">
<Border Name="Border" BorderThickness="1" Background="#FFFCFCFC">
<StackPanel>
...
</StackPanel>
<Border.Resources>
<SolidColorBrush x:Key="EventBrush" Color="Red" />
</Border.Resources>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="#FFAAAAFF" />
<Style.Triggers>
<EventTrigger RoutedEvent="Prefix:YourClassName.YourEventName">
<EventTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="Border"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{StaticResource EventBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.EnterActions>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</StackPanel>
</DataTemplate>
I'm making a zoom control (Slider) with a TextBlock indicator that tells you want the current scale factor is (kinda like in the bottom-right corner of Word).
I'm a couple days into learning WPF, and I've been able to figure out how to do most of it, but I get the sense there's a much simpler way (one which, perhaps, only involves XAML-side code rather than a bunch of mouse events being captures.
I'd like for a the text to be underlined when hovered over (to imply clickability) and for a click to reset the slider element to 1.0.
Here's what I have:
<StatusBarItem Grid.Column="1" HorizontalAlignment="Right">
<Slider x:Name="mapCanvasScaleSlider" Width="150" Value="1" Orientation="Horizontal" HorizontalAlignment="Left"
IsSnapToTickEnabled="True" Minimum="0.25" Maximum="4" TickPlacement="BottomRight"
Ticks="0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4"/>
</StatusBarItem>
<StatusBarItem Grid.Column="2">
<TextBlock Name="zoomIndicator" Text="{Binding ElementName=mapCanvasScaleSlider,Path=Value,StringFormat=0%}"
MouseDown="ResetZoomWindow" MouseEnter="zoomIndicator_MouseEnter" MouseLeave="zoomIndicator_MouseLeave"
ToolTip="Zoom level; click to reset"/>
</StatusBarItem>
private void ResetZoomWindow(object sender, MouseButtonEventArgs args)
{
mapCanvasScaleSlider.Value = 1.0;
}
private void zoomIndicator_MouseLeave(object sender, MouseEventArgs e)
{
zoomIndicator.TextDecorations = TextDecorations.Underline;
}
private void zoomIndicator_MouseLeave(object sender, MouseEventArgs e)
{
zoomIndicator.TextDecorations = null;
}
I feel as though there's a better way to do this through XAML rather than to have three separate .cs-side functions.
You could use a style trigger for the text block, like described in this other post How to set MouseOver event/trigger for border in XAML?
Working solution:
<StatusBarItem Grid.Column="2">
<TextBlock Name="zoomIndicator" Text="{Binding ElementName=mapCanvasScaleSlider,Path=Value,StringFormat=0%}"
MouseDown="ResetZoomWindow" ToolTip="Zoom level; click to reset">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.TextDecorations" Value="" />
<Style.Triggers>
<Trigger Property="TextBlock.IsMouseOver" Value="True">
<Setter Property="TextBlock.TextDecorations" Value="Underline" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StatusBarItem>
Still have to reset the zoom level manually (I think), though.
You can use VisualState (if you're using Blend its easy to edit).
Personally I prefer style triggers, unless I have to add StoryBoard animation - then I offen use VisualState
about VisualState
Typically, you wouldn't want to use a TextBlock as a button (although of course you can). You may want to consider using a more appropriate control like Button or HyperlinkButton, which have the basic mouse event handling already wired up. You can then apply whatever styles you like. A Button control, for example, can be easily styled re-templated as a TextBlock with underline on mouse-over:
<Style TargetType="Button" x:Key="LinkButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard Duration="0:0:0.1">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="content" Storyboard.TargetProperty="TextDecorations">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<TextDecorationCollection>Underline</TextDecorationCollection>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="content" Text="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Use it by referencing the style key:
<Button Content="click" Style="{StaticResource LinkButtonStyle}" />
Using this approach (rather than the alternative of adding triggers to a TextBlock) brings some advantages, which are built in to the Button control.
You can apply styles to compound states like Pressed
You can use the Click event, and its related Command property