I have Relative Panel with a bunch of Children in it. I want to change the position of children by AdoptiveTrigger in VisualStates.
The problem is when i want to change an element position from Below of other element to right of that element, I've to remove the value of Below Attached Property and then set the RightOf property to make it work,otherwise it crashes the app.
Now i want to now how to remove that Below Value?
I've tried to
a.reset the binding in Properties Window for each state and then assigning my values
b.setting that value to empty string like "";
c.Ignoring that property.
None of these worked!
please help me!
Setting the attached property RelativePanel.Below to empty can work. Besides this we can also slove this problem by setting AlignTopWith or AlignVerticalCenterWith property.
This works because the priority of AlignTopWith is higher than Below and for AlignVerticalCenterWith property, it is applied if there is no conflict. As my test, AlignVerticalCenterWith's priority is also higher than Below.
For more information, please see the Conflicting relationships section in RelativePanel class.
Following is the sample I used to test:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BlueRect.(RelativePanel.Below)" Value="" />
<Setter Target="GreenRect.(RelativePanel.RightOf)" Value="BlueRect" />
<Setter Target="GreenRect.(RelativePanel.Below)" Value="RedRect" />
<!--<Setter Target="GreenRect.(RelativePanel.AlignVerticalCenterWith)" Value="BlueRect" />-->
<!--<Setter Target="GreenRect.(RelativePanel.AlignTopWith)" Value="BlueRect" />-->
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<RelativePanel>
<Rectangle x:Name="RedRect"
Width="100"
Height="100"
Fill="Red" />
<Rectangle x:Name="BlueRect"
Width="100"
Height="200"
Fill="Blue"
RelativePanel.Below="RedRect"
RelativePanel.RightOf="RedRect" />
<Rectangle x:Name="GreenRect"
Width="100"
Height="100"
Fill="Green"
RelativePanel.Below="BlueRect"
RelativePanel.RightOf="RedRect" />
</RelativePanel>
</Grid>
It works like:
You can do
<Setter Target="SomeElement.(RelativePanel.Below)" Value="{x:Null}" />
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.
I'm creating a Hamburger navigation UI for an UWP using Visual triggers and I get this error.
"An animation is trying to modify an object named 'HamburgerButtonMobile', but no such object can be found in the Page."
This is my code
<VisualState.Setters>
<Setter Target="HamburgerButtonMobile.Visibility" Value="Collapsed" />
</VisualState.Setters>
<Pivot x:Name="MoviesPivot" Title="MOVIES">
<Pivot.TitleTemplate>
<DataTemplate>
<RelativePanel>
<TextBlock Text="{Binding}" FontSize="72" Foreground="#FF21B255" FontWeight="Light" RelativePanel.AlignRightWithPanel="True" x:Name="titleName"/>
<Button x:Name="HamburgerButtonMobile" FontFamily="Segoe MDL2 Assets" Content="" Width="50" Height="50" Background="Transparent" Click="HamburgerButtonMobile_Click" RelativePanel.LeftOf="titleName" RelativePanel.AlignVerticalCenterWithPanel="True" />
</RelativePanel>
</DataTemplate>
</Pivot.TitleTemplate>
The reason that your Visual Trigger can't see the item is because the item you're trying to access lives inside of a DataTemplate. It 'Doesn't Exist' yet
I THINK what you want to do is add the visual trigger to the RelativePanel.
Honestly it's hard to say without seeing more code.
<RelativePanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="BlahNameState">
<VisualState.Setters>
<Setter Target="HamburgerButtonMobile.Visibility" Value="Collapsed" />
</VisualState.Setters>
I try to create a custom ComboBox called MyComboBox. It has a button for switching between the previous and next items.
I store the Background color of the base in BaseBackground. This is useful, as I don't want the FrontGlyph to have the Background inherited from the templated parent.
This is my WPF code:
<Style x:Key="{x:Type local:MyComboBox}" TargetType="{x:Type local:MyComboBox}">
</Style.Resources>
<Setter Property="Focusable" Value="False" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.CanContentScroll" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyComboBox}">
<Grid Cursor="Hand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- A glyph will come here -->
<ColumnDefinition Width="*" /> <!-- The base combo box can take as much space as it can. -->
</Grid.ColumnDefinitions>
<Path x:Name="FrontGlyph" Grid.Column="0" Data="M 0.0 16.0 L 6.0 0.0 L 6.0 16.0 Z" Fill="{TemplateBinding BaseBackground}" Stretch="Fill" />
<Grid x:Name="BaseComboBox" Grid.Column="1" Background="{TemplateBinding BaseBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- Previous item -->
<ColumnDefinition Width="Auto" /> <!-- Next item -->
<ColumnDefinition Width="*" /> <!-- Content Presenter -->
<ColumnDefinition Width="Auto" /> <!-- Drop down button -->
</Grid.ColumnDefinitions>
<Button x:Name="Prev" Grid.Column="0" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 4.5 0.5 L 0.5 4.5 L 4.5 8.5 Z" Fill="Black" />
</Button>
<Button x:Name="Next" Grid.Column="1" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 0.5 0.5 L 4.5 4.5 L 0.5 8.5 Z" Fill="Black" />
</Button>
<ContentPresenter x:Name="ContentSite" Grid.Column="2" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
<ToggleButton x:Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="3" Focusable="false" ClickMode="Press" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" />
<Popup x:Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid x:Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" BorderThickness="1" BorderBrush="{TemplateBinding Background, Converter={StaticResource LightenBrushColor}, ConverterParameter=0.5}" Background="{TemplateBinding Background}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BaseBackground" Value="Goldenrod" /> <!-- This does not work -->
<!--<Setter TargetName="FrontGlyph" Property="Fill" Value="Goldenrod" />
<Setter TargetName="BaseComboBox" Property="Background" Value="{Binding Path=Fill, ElementName=FrontGlyph}" />--> <!-- These 2 lines do work. -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the source code of MyComboBox:
class MyComboBox : ComboBox
{
public static readonly DependencyProperty BaseBackgroundProperty;
public SolidColorBrush BaseBackground { get { return (SolidColorBrush)GetValue(BaseBackgroundProperty); } set { SetValue(BaseBackgroundProperty, value); } }
static MyComboBox()
{
BaseBackgroundProperty = DependencyProperty.Register("BaseBackground", typeof(SolidColorBrush), typeof(MyComboBox), new FrameworkPropertyMetadata(Brushes.Lime, FrameworkPropertyMetadataOptions.AffectsRender, OnBaseBackgroundPropertyChanged));
}
public MyComboBox()
{
DefaultStyleKey = typeof(MyComboBox);
}
private static void OnBaseBackgroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// This is not called when the trigger tries to set BaseBackground when the mouse is over the control
}
}
When I hover the mouse over the control, it should change colors. I should be able to do this via changing the BaseBackground, since both FrontGlyph and BaseComboBox have their background color bound to that. Nevertheless the code compiles but the change does not happen. Not just on the UI, but if I debug the code, I don't see the change in C# either.
If I change the background color of the FrontGlyph and bind the BaseComboBox.BackgroundColor to that, it works nicely.
Could someone explain why the changes of my custom property BaseBackground is not registered? It must not be the standard "Implement the INotifyPropertyChanged interface." issue, as I use Brush as my property and Brushes work pretty well everywhere else. :)
My implementation might look silly. Well, I'm kinda new to WPF. Plus I didn't want to burden you with the whole implementation, just tried to replicate the critical parts.
UPDATE
I've found out that in my source code I set BaseBackground = new SolidColorBrush(...) if some conditions are fulfilled. If I remove this line of code, now the triggers work and BaseBackground gets assigned the Goldenrod color.
But I wonder, why changing a DependencyProperty from C# code prevents it working from XAML markup. Besides, I need both of them to work.
Thank you.
Summary
I think what you probably want to do is register the BaseBackgroundProperty under the name "BaseBackground" (rather than "Background") since your xaml is trying to set a property with that name.
Details
Looking at your code, I think the reason your MyComboBox.BaseBackground property is never set is because it is being registered with the dependency property system using the name "Background". This means you have a property registered in a base class as "Background" (Control.BackgroundProperty), and a property registered in your derived class as "Background" (MyComboBox.BaseBackgroundProperty). So in theory, setting "Background" in xaml should end up setting MyComboBox.BaseBackgroundProperty while setting "Control.Background" should end up setting the Control.Background property.
While that works in theory, I have no idea if it works in practice. It is also not really the way to go about doing things. You could override the metadata for Control.BackgroundProperty in your type initializer (aka static constructor) if you wanted to modify the existing property for your class in some way, but I don't think that is what your intention is here. Your xaml is trying to set the non-existent property named "BaseBackground".
Based on your question update, it looks like what you are doing is setting a local value on the dependency property, which might be breaking the template binding. This can happen with one way bindings in certain situations where the target gets set by some other means. It seems like you are setting the source in this case, but it is possible that it is causing the binding to break.
You can debug the binding by setting the PresentationTraceSources.TraceLevel property on it to "High". (You might need to use the standard {Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay} syntax rather than the {TemplateBinding} shortcut syntax in order to attach the property – I am not sure.) Then, run the application and look at the debug output. It will tell you if the binding gets broken.
If the binding is in fact being broken, there are different things you can do to fix it, depending on your use cases.
You can set the binding mode to TwoWay to keep it from breaking.
You can try only setting it from code using SetCurrentValue instead of SetValue, as many controls tend to do when modifying their own dependency properties from code.
Potentially related information:
DependencyProperty Value Precedence
In WPF Why Doesn't TemplateBinding Work Where Binding Does?
Debugging Data Bindings
Control Authoring Overview
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