wpf string format integer 1234 to 12:34 only in XAML - wpf

I have 1234 value and I have to show it as 0012:34 and when user clicks on that text box to edit the value, it should display only 1234 and when tabs out it should go back to 0012:34. If I use a converter, it does not change the format when got focus. I have this text box inside a data template and cannot access it in code behind also, meaning, I cannot do the formatting in Got_Focus event. Can anyone help with the formatting please?
I can use int or string as the datatype.
Thanks,
Rosy

You can use WatermarkTextBox from Extended WPF Toolkit:
<xctk:WatermarkTextBox Text="{Binding Value}" Watermark="{Binding Value, Mode=OneWay, Converter={StaticResource YourValueConverter}}" />

As an alternative to a watermark textbox you could use a behavior.
System.Windows.Interactivity needs to be referenced.
Example:
Xaml:
<Window x:Class="WatermarkTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:WatermarkTextBox="clr-namespace:WatermarkTextBox" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Width="300" Height="30">
<i:Interaction.Behaviors>
<WatermarkTextBox:WatermarkBehavior />
</i:Interaction.Behaviors>
</TextBox>
<TextBox Grid.Row="1" Width="300" Height="30" />
</Grid>
</Window>
Behavior:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WatermarkTextBox
{
public class WatermarkBehavior : Behavior<TextBox>
{
private string _value = string.Empty;
protected override void OnAttached()
{
AssociatedObject.GotFocus += OnGotFocus;
AssociatedObject.LostFocus += OnLostFocus;
}
protected override void OnDetaching()
{
AssociatedObject.GotFocus -= OnGotFocus;
AssociatedObject.LostFocus -= OnLostFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
AssociatedObject.Text = _value;
}
private void OnLostFocus(object sender, RoutedEventArgs e)
{
_value = AssociatedObject.Text;
AssociatedObject.Text = string.Format("Watermark format for: {0}", _value);
}
}
}

Related

Specify column added order in a WPF Grid in code behind?

Adding a column to a grid in code behind is easy:
col10 = new ColumnDefinition();
col10.SharedSizeGroup = "column1";
When you add the column it adds to the end of the grid for example you have a grid with columns A and B, you use the code above and a new column (C) and it is added as A B C.
Is it possible to set it up like this?
C A B
Instead on adding to the end its added to the front?
Thanks
ColumnDefinitions are like any other Collection and support the IList<> interface.
So just use an insert method to control added order.
ColumnDefinition myColumn = new ColumnDefintion();
Grid myGrid = new Grid();
myGrid.ColumnDefinitions.Insert(0, myColumn);
Try this:
XAML file:
<Window x:Class="DataGridAddColumn.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<ComboBox Name="cbWhere" Width="100" VerticalAlignment="Center">
<ComboBoxItem>Front</ComboBoxItem>
<ComboBoxItem>End</ComboBoxItem>
</ComboBox>
<TextBlock Text="Name:" VerticalAlignment="Center" Margin="10,0,0,0" />
<TextBox Name="tbName" MinWidth="100" VerticalAlignment="Center" />
<Button Content="Create" VerticalAlignment="Center" Margin="10,0,0,0" Click="Button_Click" />
</StackPanel>
<DataGrid Grid.Row="1" Name="grid" />
</Grid>
</Window>
Code-behind:
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DataGridAddColumn
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void AddColumn(DataGrid grid, string name, int where)
{
if (where == 0)
{
grid.Columns.Insert(0, new DataGridTextColumn{Header = name});
}
else
{
grid.Columns.Add(new DataGridTextColumn { Header = name });
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AddColumn(grid, tbName.Text, cbWhere.SelectedIndex);
}
}
}

DataGrid - Mouse enter + leave row events

I would like to subscribe to the mouse enter and leave events of a DataGridRow.
My XAML for the DataGrid looks like this at the moment:
<toolkit:DataGrid ItemsSource="{Binding DisplayedSearchResults}"
caliburn:Message.Attach="[Event MouseDoubleClick] = [OpenDocument()]"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}" Margin="7"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
IsReadOnly="True" Grid.Row="0"
Sorting="ResultsDataGrid_Sort">
<toolkit:DataGrid.Resources>
<Style TargetType="Button"></Style>
</toolkit:DataGrid.Resources>
</toolkit:DataGrid>
How can I do this using Caliburn if possible, but code-behind if not?
I ended hooking up in the code-behind:
MyView.xaml.cs:
public partial class MyView : UserControl
{
public SearchResultsView()
{
InitializeComponent();
SearchResultsGrid.LoadingRow += DataGrid_PreparingRow;
}
public void DataGrid_PreparingRow(object sender, DataGridRowEventArgs args)
{
args.Row.MouseEnter += Row_MouseEnter;
args.Row.MouseLeave += Row_MouseLeave;
}
public void Row_MouseEnter(object sender, MouseEventArgs args)
{
// do some stuff
}
public void Row_MouseLeave(object sender, MouseEventArgs args)
{
// do some stuff
}
}
MyView.xaml:
<UserControl x:Class="MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<toolkit:DataGrid Name="SearchResultsGrid" />
</Grid>
</UserControl>

Binding to a property of a borders child

I have implemented my own simple version of a navigation window, mainly because navigation windows journal does not give me control over how many children can exist. So I am using a border inside a window and changig its child everytime. As children I am using a UserControl. I want to bind the title of my Window to the Title property of my current child. Somehow I cannot figure out a way to do it.
MainWindow XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="525"
Height="350"
Background="AliceBlue"
Title="{Binding Path=Child.Title,
ElementName=borderContent}">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="<-" x:Name="btnBack" />
<Button Content="->" x:Name="btnForward" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Content="1" Click="Button_Click_1" />
<Button Content="2" Click="Button_Click_2" />
</StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Border x:Name="borderContent" />
</ScrollViewer>
</DockPanel>
MainWindow Code behind:
using System;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.borderContent.Child = new ContentPage("Title 1");
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
this.borderContent.Child = new ContentPage("TITLE 2");
}
}
}
UserControl XAML:
<UserControl x:Class="WpfApplication1.ContentPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Title}" />
</Grid>
User Control Code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Content.xaml
/// </summary>
public partial class ContentPage : UserControl
{
public string Title
{
get { return (string)this.GetValue(ContentPage.TitleProperty); }
set { this.SetValue(ContentPage.TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ContentPage), new UIPropertyMetadata(string.Empty));
public ContentPage(string Title)
{
this.Title = Title;
InitializeComponent();
}
}
}
Somehow the binding inside the UserControl is also not working. What am I doing wrong?
The problem is that the Child property of a Borderisn't a DependencyProperty so there is no change notification. You'll have to update the Binding manually everytime you change the Child
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.borderContent.Child = new ContentPage("Title 1");
UpdateTitleBindingExpression();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
this.borderContent.Child = new ContentPage("TITLE 2");
UpdateTitleBindingExpression();
}
private void UpdateTitleBindingExpression()
{
BindingExpressionBase beb = BindingOperations.GetBindingExpressionBase(this, Window.TitleProperty);
if (beb != null)
{
beb.UpdateTarget();
}
}
I'm not sure why you are doing what you are doing, but regarding your question:
change "Source" to "RelativeSource"
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Title}" />
</Grid>
That should fix the binding issue
Edit:
When you really want to do it that way, you could make the borderContent element an ContentControl and use the Content property instead. Since this is an DependencyProperty you're binding will work:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="525"
Height="350"
Background="AliceBlue"
Title="{Binding Content.Title, ElementName=borderContent}">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="<-" x:Name="btnBack" />
<Button Content="->" x:Name="btnForward" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Content="1" Click="Button_Click_1" />
<Button Content="2" Click="Button_Click_2" />
</StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl x:Name="borderContent" />
</ScrollViewer>
</DockPanel>
</Window>

Two way binding use a user control...binding to object, not an element?

I created an object with a simple property with a default value. I then created a user control that has a text box in it. I set the datacontext of the user control to the object.
The text box correctly shows the properties default value but I can't seem to update the property value when the user changes the text box value. I created a simple project to illustrate my code.
Thanks for the help!!
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private string _titleValue;
public string TitleValue
{
get
{
return _titleValue;
}
set
{
_titleValue = value;
textBox1.Text = _titleValue;
}
}
public static readonly DependencyProperty TitleValueProperty = DependencyProperty.Register(
"TitleValue", typeof(string), typeof(UserControl1), new FrameworkPropertyMetadata(new PropertyChangedCallback(titleUpdated))
);
//Don't think I should need to do this!!!
private static void titleUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UserControl1)d).TitleValue = (string)e.NewValue;
}
}
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="94,97,0,0" Name="textBox1" VerticalAlignment="Top" Width="120"
Text="{Binding Path=TitleValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var dummy = new DummyObject("This is my title.");
userControl11.DataContext = dummy;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("The value is: " + ((DummyObject)userControl11.DataContext).Title);
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:WpfApplication1">
<Grid>
<my:UserControl1 HorizontalAlignment="Left" Margin="95,44,0,0" x:Name="userControl11" VerticalAlignment="Top" Height="191" Width="293"
TitleValue="{Binding Path=Title, Mode=TwoWay}"/>
<Button Content="Check Value" Height="23" HorizontalAlignment="Left" Margin="20,12,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>
The DataContext on your usercontrol isn't set. Specify a Name for it (I usually call mine "ThisControl") and modify the TextBox's binding to Text="{Binding ElementName=ThisControl, Path=TitleValue, Mode=TwoWay}". You can also set the DataContext explicitly, but I believe this is the preferred way.
It seems like the default DataContext should be "this", but by default, it's nothing.
[edit] You may also want to add , UpdateSourceTrigger=PropertyChanged to your binding, as by default TextBoxes' Text binding only updates when focus is lost.

How to access ListBox dynamically-created-items' properties from code-behind?

XAML:
<Window x:Class="WpfApp_ListBoxTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<ListBox Name="lb" Margin="0,0,0,70"></ListBox>
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,41" Name="btnAdd" VerticalAlignment="Bottom" Content="Add item" Width="75" Click="btnAdd_Click"></Button>
<TextBox Height="23" Margin="93,0,12,41" Name="txtInput" VerticalAlignment="Bottom" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,12" Name="btnGet" VerticalAlignment="Bottom" Content="Get value" Width="75" Click="btnGet_Click"></Button>
<TextBox Height="23" Margin="93,0,12,12" Name="txtReturn" VerticalAlignment="Bottom" IsReadOnly="True" />
</Grid>
</Window>
Csharp:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
namespace WpfApp_ListBoxTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
TextBox txt = new TextBox();
txt.Width = 200;
txt.Text = txtInput.Text;
lb.Items.Add(txt);
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
// What do I need to write here to get the value of the Text property of the selected TextBox?
}
}
}
And screenshot: (Sorry I'm not allowed to post picture directly)
http://i825.photobucket.com/albums/zz180/mGlushed/get_listbox_item_property.png
(In the picture above, I want to get the value "b" when I click the "Get value" button.)
I would like to know if there is a simple way to achieve this.
I'm new to WPF, so I only know to do this the long way, which is: Create an array. Everytime a new TextBox is created, add it into the array. Then access the TextBox'es through the array. But that doesn't sound very optimal, I think.
The 'WPF Way' of doing what you want is to use data binding:
Define a class with a string property called Text.
Create a collection of that class.
Bind your list box ItemsSource to the collection.
Create a DataTemplate that shows a TextBox with its Text property bound using {Binding Path=Text}.
In btnAdd_Click add an item to the collection (not directly to the ListBox)
In btnGet_Click you can get the text entered by casting ListBox.SelectedItem to your class and getting its Text property.
Example:
The simple class:
public class VMObject
{
public VMObject(string text)
{
Text = text;
}
public string Text { get; set; }
}
The window code-behind:
public partial class Window1 : Window
{
public ObservableCollection<VMObject> VM { get; set; }
public Window1()
{
VM = new ObservableCollection<VMObject>();
InitializeComponent();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
VM.Add(new VMObject(txtInput.Text));
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
if (lb.SelectedItem == null)
MessageBox.Show("No item is selected!");
txtReturn.Text = ((VMObject)lb.SelectedItem).Text;
}
}
The XAML:
<Window x:Class="lbtest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Window"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="TextBoxTemplate">
<TextBox Text="{Binding Path=Text}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="lb" Margin="0,0,0,70"
ItemsSource="{Binding ElementName=Window, Path=VM}"
ItemTemplate="{StaticResource TextBoxTemplate}" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,41"
Name="btnAdd" VerticalAlignment="Bottom"
Content="Add item" Width="75" Click="btnAdd_Click" />
<TextBox Height="23" Margin="93,0,12,41"
Name="txtInput" VerticalAlignment="Bottom" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,12"
Name="btnGet" VerticalAlignment="Bottom"
Content="Get value" Width="75" Click="btnGet_Click" />
<TextBox Height="23" Margin="93,0,12,12"
Name="txtReturn" VerticalAlignment="Bottom" IsReadOnly="True" />
</Grid>
</Window>
for a checkbox item:
private void chk_Checked(object sender, RoutedEventArgs e)
{
CheckBox chk = (CheckBox)sender;
MessageBox.Show(chk.Content.ToString());
}
No need for TextBox:s. ListBox handle strings fine.
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
// No need to create TextBox, ListBox handle strings fine.
lb.Items.Add(txtInput.Text);
}
private void btnGet_Click(object sender, RoutedEventArgs e)
{
// No selection, but button has been pressed.
if(lb.SelectedItem == -1)
return;
// Get selected item.
txtReturn.Text = (string)lb.SelectedItem;
/* If you change ListBox selection mode to multiple
* you can get all selected items by using foreach loop.
foreach (Object selected in lb.SelectedItems)
{
txtReturn.Text += (string) selected;
}
*/
}
If you just want to get the Text property of the selected TextBox (admiting your ListBox is in single selection mode) it it quite simple:
private void btnGet_Click(object sender, RoutedEventArgs e)
{
if(lb.SelectedItem != -1)
{
TextBox selectedTextBox = (TextBox)lb.SelectedItem;
txtReturn.Text = selectedTextBox.Text;
}
}
But if you want to implement the pretty WPF way, you should follow the Aviad P. solution, my solution do it well too.
Regards.
EDIT: If do not have a real need of TextBox functionalities, but only a string container, so follow Tuukka's solution.

Resources