TextBox Customization Malfunction - c#

I needed to create a textBoox that completely blends with the background so I used this XAML:
<TextBox Text="Hello" VerticalAlignment="Bottom" Name="txtAsk" FontFamily="Consolas" Margin="8,0,0,0" TextWrapping="Wrap" AutoWordSelection="True" KeyDown="onEnter">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="0"/>
<TextBox Margin="4" Text="{TemplateBinding Text}" BorderThickness="0" Background="{x:Null}" SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</TextBox.Template>
</TextBox>
However, now the problem is, when I attached a button to the interface and triggered it to show the content of the TextBox in a message window, it only shows the default text even after changing the value inside the text box!
The button code is as follows:
private void btnTriggerEnter_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(txtAsk.Text);
}
I am not sure where I screwed up. Please comment if any other detail is required.
My MainWindow has this code:
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
Thanks.

You should use:
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}"
That's because TemplateBinding only gives you a OneWay source (in the underlying Binding), you need TwoWay.
http://blogs.msdn.com/b/liviuc/archive/2009/12/14/wpf-templatebinding-vs-relativesource-templatedparent.aspx
WPF TemplateBinding vs RelativeSource TemplatedParent

Related

Is it possible to change properties on the validation error template in WPF?

I'm using the Validation.ErrorTemplate property in my XAML view to display an icon next to any invalid control.
For one particular control in my view, I need to set the icon in a slightly different position. Is it possible for me to use the same control template, but somehow update the Margin property for a certain control?
Here's my (abridged) code:
<UserControl>
<UserControl.Resources>
<ControlTemplate x:Key="ValidationTemplate" TargetType="Control">
<DockPanel>
<Grid
DockPanel.Dock="Right"
Height="16"
Margin="10,0,0,0"
VerticalAlignment="Center"
Width="16">
<Image
AutomationProperties.AutomationId="_validationIcon"
Source="{x:Static icons:Icons.ValidationIcon}"
ToolTip="{Binding Path=ErrorContent}" />
</Grid>
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
<ItemsControl Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
</UserControl>
The only way I've managed to achieve what I need is to create a new ControlTemplate just for the control that requires a different icon placement. I would rather reuse my original control template if possible.
I had exactly same requirement in my old project. I solved it using an attached dependency property:
public static class ErrorTemplateProperties
{
public static readonly DependencyProperty ErrorMarginProperty = DependencyProperty.RegisterAttached
(
"ErrorMargin",
typeof(Thickness),
typeof(ErrorTemplateProperties),
new FrameworkPropertyMetadata(new Thickness(10,0,0,0))
);
public static Thickness GetErrorMargin(DependencyObject obj)
{
return (Thickness)obj.GetValue(ErrorMarginProperty);
}
public static void SetErrorMargin(DependencyObject obj, Thickness value)
{
obj.SetValue(ErrorMarginProperty, value);
}
}
add it to ValidationTemplate:
<ControlTemplate x:Key="ValidationTemplate" TargetType="Control">
<DockPanel>
<Grid
DockPanel.Dock="Right"
Margin="{Binding Path=AdornedElement.(local:ErrorTemplateProperties.ErrorMargin), ElementName=ui}"
Height="16"
VerticalAlignment="Center"
Width="16">
<Image
AutomationProperties.AutomationId="_validationIcon"
Source="{x:Static icons:Icons.ValidationIcon}"
ToolTip="{Binding Path=ErrorContent}" />
</Grid>
<AdornedElementPlaceholder x:Name="ui"/>
</DockPanel>
</ControlTemplate>
and then optionally change on required ui elements:
<ItemsControl helpers:ErrorTemplateProperties.ErrorMargin="0,0,0,0"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}" />
I also found a workaround with DynamicResources but attached DP are more flexible and laconic in my opinion

Binding Button Click Event to a Method in ViewModel with Interaction.Triggers when Buttons are dynamically created at runtime

In my C# WPF MVVM pattern application, I have an ItemsControl in my View that draws Lines and Buttons on a Canvas based on a bound ItemsSource, defined in XAML as below:
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
.
.
.
<ItemsControl
x:Name="DiagramViewCanvas"
ItemsSource="{Binding DiagramObjects, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:LineObject}">
<Line
X1="{Binding XStart}"
Y1="{Binding YStart}"
X2="{Binding XEnd}"
Y2="{Binding YEnd}"
Stroke="White"
StrokeThickness="1"
SnapsToDevicePixels="True"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ButtonObject}">
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True">
</Button>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Black" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding XPosition, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="Canvas.Top" Value="{Binding YPosition, UpdateSourceTrigger=PropertyChanged}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
This code works completely fine. My question is how to bind the Buttons' Click event to a method in the ViewModel (MainWindowViewModel).
Option 1 (which I don't want to use due MVVM pattern): If I try a simple Click event definition as below ...
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True"
Click="OnButtonClick"/>
... where OnButtonClick is defined in my XAML codebehind, the OnButtonClick method is successfully called and executed for each Button that is created at runtime. It works fine.
Option 2: However, if I try to use Interaction.Triggers as below (which is the approach I regularly use without any problems in my code) to avoid placing code in code behind ...
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding}" MethodName="OnButtonClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
... where OnButtonClick is defined in my MainWindowViewModel ...
public void OnButtonClick(object sender, RoutedEventArgs e)
{
if (sender is Button btn)
{
// do something
}
}
... I get the following error:
System.ArgumentException: 'Could not find method named 'OnButtonClick' on object of type 'ButtonObject' that matches the expected signature.'
Question 1: Am I making a basic mistake in my implementation of interaction triggers (I have many other interaction triggers in my code that work completely fine)? Or is it that Interaction.Triggers do not work in this scenario where the Buttons are created dynamically at runtime?
Question 2: Should I be using ICommand instead (for example as mentioned in Binding Commands to Events?)?
Thanks for any direction on what I am doing wrong.
Found a solution using Interaction.Triggers:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=DataContext}" MethodName="OnButtonClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>

WPF C# Commands bound to userControl won't fire

So, I have created a UserControl which really is an "advanced button".
I have implemented Dependency Properties, one of which is ICommand, that is supposed to be bindable further when control is used in an actual Window.
However, for some reason the Command doesn't work.
When I tried an exact same approach on a regular button, everything worked fine (thus it's not the fault of my DelegateCommand implementation or my ViewModel).
I tried to followup on why the bound command doesn't fire, but I couldn't find a reliable reason for it not to.
Here is my UserControl XAML:
<Window x:Class="NoContact.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NoContact"
mc:Ignorable="d"
xmlns:userControls="clr-namespace:NoContact.UserControls"
Title="MainWindow" Height="800" Width="960" Background="#22282a">
<Border BorderThickness="1" BorderBrush="#ffcd22" Margin="10,10,10,10">
<Grid>
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}">
<DockPanel Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth}">
<Image Source="{Binding ElementName=ImageButtonUC, Path=Image}" DockPanel.Dock="Left"
Height="{Binding ElementName=ImageButtonUC, Path=ActualHeight, Converter={StaticResource HeightConverter}}"
Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth, Converter={StaticResource HeightConverter}}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding ElementName=ImageButtonUC, Path=Text}" FontSize="17" VerticalAlignment="Center" />
</DockPanel>
</Button>
</Grid>
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And here is my UserControl code-behind:
public partial class ImageButton : UserControl
{
// OTHER IRRELEVANT CLASS PARAMETERS ARE HERE
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// OTHER IRRELEVANT DEPENDENCY PROPERTIES ARE HERE
public static DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(ImageButton));
public ImageButton()
{
InitializeComponent();
}
}
Finally, this is how I use my control:
<userControls:ImageButton x:Name="phoneButton" ClickCommand="{Binding Path=MyButtonClickCommand}" Style="{StaticResource phoneImageButtonUCStyle}" Text="Telefon" Width="200" Height="100" VerticalAlignment="Top" />
The DataContext of the control is set on a stackpanel, that is wrapped around my control. I have also tried setting it directly on the control, no effect.
Again, doing the same on a regular button works just fine.
I have finally solved the problem - and it was pretty trivial.
I had ommited the most important binding - UserControl's button Command to UserControl's Dependency property.
Changing it like this made it work:
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}"
Command="{Binding ElementName=ImageButtonUC, Path=ClickCommand}">

WPF: What is the event fired when auto creating a new control from datatemplate?

I have an ObservableCollection<Tag> Tags where the Tag class contains only a string Content property. I've created a DataTemplate that displays all tags and shows small buttons to delete and add new tags.
<DataTemplate>
<Border BorderThickness="1" BorderBrush="#676B6E" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Tag="{Binding}" Padding="0" Margin="2,0"/>
<Button Style="{StaticResource RibbonButton}" Click="ButtonRemoveTagClick" Tag="{Binding}" Padding="0">
<Image Height="12" Width="12" Source="/My Application;component/Resources/cross.png" />
</Button>
</StackPanel>
</Border>
</DataTemplate>
When I add a new Tag to the collection I want the autocreated textbox to automatically select all the text inside and grab focus.
Is there an appropriate event I can handle on the textbox itself, or is there a better way to handle this?
I tried with WpfExtendedToolkit.AutoSelectTextBox but does not work the way I want.
In this case the Loaded event of the TextBox did the trick.
private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
TextBox tb = sender as TextBox;
tb.Focus();
tb.SelectAll();
}
Thanks to Clemens for the quick response.

Trying to pin image on map with onclick event to page, based on binding.

I build this code so that i have multiple pins on the map. locations and text based on binding:
<Grid x:Name="LayoutRoot" Background="Transparent">
<maps:Map x:Name="myMap" Loaded="myMap_Loaded">
<toolkit:MapExtensions.Children>
<toolkit:MapItemsControl Name="Items">
<toolkit:MapItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Pushpin GeoCoordinate="{Binding Coordinate}" Tap="Pushpin_Tap_1" >
<toolkit:Pushpin.Template >
<ControlTemplate >
<Canvas>
<Image Source="/App;component/Assets/pin.png"
Width="48" Height="102"
Canvas.Left="-20" Canvas.Top="-102" />
<Border Background="Black" Width="200" Visibility="Visible" x:Name="border1" HorizontalAlignment="Center" >
<StackPanel x:Name="_Stack1" >
<TextBlock x:Name="TextBlock1"
Text="{Binding ID}"
Canvas.Top="-45"
Canvas.Left="5"
Style="{StaticResource PhoneTextNormalStyle }"
Foreground="#FF51FF00" />
<TextBlock
Text="{Binding Name}"
Canvas.Top="-25"
Canvas.Left="5"
Style="{StaticResource PhoneTextContrastStyle }"
Foreground="Red" />
</StackPanel>
</Border>
</Canvas>
</ControlTemplate>
</toolkit:Pushpin.Template>
</toolkit:Pushpin>
</DataTemplate>
</toolkit:MapItemsControl.ItemTemplate>
</toolkit:MapItemsControl>
</toolkit:MapExtensions.Children>
</maps:Map>
</Grid>
Now my question is how i can make a Tab event for the pushpin based on the TextBlock1.Text (I can`t "reach" the textblock1.text in the .cs part)
private void Pushpin_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
NavigationService.Navigate(new Uri("/ExtraPage/Info.xaml?selectedItem=" + TextBlock1.Text(THIS WONT WORK) , UriKind.Relative));
}
What is the best way to do this? Or am I doing it all wrong?
Thanks in advance
You can't access it because it's in the template.
If you want the tap event on TextBlock, just add it there instead of adding it to the pushpin. But I assume that's not really what your need.
If you want the Text value from TextBlock1, what you really want is the ID which is bound to that TextBlock. The sender for that event handler that you have is (probably) a Pushpin. So, what you really want is the DataContext of that Pushpin and the ID property of that DataContext, and from what I can tell, the DataContext is some sort of a location object, so I'll call it Location.
var id = ((sender as Pushpin).DataContext as Location).ID;
And that's what's in your TextBlock - id which you can easily use.
Change 'Location' to whatever your class is.

Categories