How to set Grid Row with RowDefinition name? - wpf

I'm organizing my grid with RowDefinitions and ColumnDefinition, but forever when I want add a new RowDefinition in before actual any RowDefinition, I need reorganize Grid.Row of all controls
I saw RowDefinition and ColumnDefinition has a Name property, so I think is possible define Grid.Row with RowDefinition name or not? If is possible, How do
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="RowDictTitle" Height="27"/>
<RowDefinition Name="RowSearchWord" Height="27"/>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<!--Row 1-->
<TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Row="1"/>
<TextBox Name="Search" Grid.ColumnSpan="2" Margin="50,2,10,2"/>
<!--Row 2-->
<ListBox Name="Words" Grid.Row="2" Margin="10"/>
</Grid>
I want make below
<TextBlock Text="Word" Grid.Row="RowSearchWord"/>

Disclaimer: This answer is kind of a self-advertisement within the constraints alluded to by this meta post. It advertises a free open source project that I (at the time of writing this) do not earn any money with. The only gain is the knowledge that my time for writing the described control was not wasted if it helps some future visitors of this SO question.
I had exactly the same thoughts. That is why, not too long ago, I wrote a custom grid class that uses named columns and rows.
I put it on Codeplex under the MIT license: Name-Based Grid project
With that control, you can rewrite your Xaml source code as follows:
<nbg:NameBasedGrid>
<nbg:NameBasedGrid.RowDefinitions>
<nbg:ColumnOrRow Name="RowDictTitle" Height="27"/>
<nbg:ColumnOrRow Name="RowSearchWord" Height="27"/>
<nbg:ColumnOrRow Name="List"/>
<nbg:ColumnOrRow Height="50"/>
</nbg:NameBasedGrid.RowDefinitions>
<nbg:NameBasedGrid.ColumnDefinitions>
<nbg:ColumnOrRow Width="1*" Name="Left"/>
<nbg:ColumnOrRow Width="2*" Name="Right"/>
</nbg:NameBasedGrid.ColumnDefinitions>
<!--Row 1-->
<TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowSearchWord"/>
<TextBox Name="Search" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowDictTitle" nbg:NameBasedGrid.ExtendToColumn="Right" Margin="50,2,10,2"/>
<!--Row 2-->
<ListBox Name="Words" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="List" Margin="10"/>
</nbg:NameBasedGrid>
Advantage: You will be able to reference columns and rows (including column and row spans!) by name - no more counting of columns or rows, no more updating column or row spans when the layout changes.
Disadvantage: You will need to explicitly state names for all columns and rows, as numerical references are not supported at all in NameBasedGrid.

Nice idea but since the Grid.Row attached property is an integer this is not possible.
See http://msdn.microsoft.com/en-us/library/system.windows.controls.grid.row.aspx
However, it may possible to create a helper that takes the name of the grid row, finds the row object and returns its row index.

I was looking for the same thing. Could not find exacly what I was looking for so i came up with my own solution using attached properties.
I created a specialized grid with attached properties for RowName and ColumnName.
(In this example i only implemented RowName)
using System.Windows;
using System.Windows.Controls;
namespace GridNamedRows.CustomControl
{
public class MyGrid: Grid
{
public static readonly DependencyProperty RowNameProperty =
DependencyProperty.RegisterAttached(
"RowName",
typeof(string),
typeof(MyGrid),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsParentArrange,
new PropertyChangedCallback(RowNameChanged)),
new ValidateValueCallback(IsStringNotNull));
private static bool IsStringNotNull(object value)
{
return (value as string) != null;
}
private static void RowNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
{
return;
}
if (!(d is UIElement)) return;
Grid parent = ((FrameworkElement)d).Parent as Grid;
if (parent == null) return;
//Find rowname
for (int i = 0; i < parent.RowDefinitions.Count; i++)
{
if (parent.RowDefinitions[i].Name == e.NewValue.ToString())
{
Grid.SetRow((UIElement)d, i);
break;
}
}
}
public static string GetRowName(DependencyObject target)
{
return (string)target.GetValue(RowNameProperty);
}
public static void SetRowName(DependencyObject target, string value)
{
target.SetValue(RowNameProperty, value);
}
}
}
It can be used in xaml like this.
<Window xmlns:CustomControl="clr-namespace:GridNamedRows.CustomControl" x:Class="GridNamedRows.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">
<CustomControl:MyGrid>
<Grid.RowDefinitions>
<RowDefinition Name="firstRow"/>
<RowDefinition Name="secondRow"/>
<RowDefinition Name="thirdRow"/>
</Grid.RowDefinitions>
<TextBox Text="one" CustomControl:MyGrid.RowName="secondRow"/>
<TextBox Text="two" Grid.Row="2"/>
<TextBox Text="three" CustomControl:MyGrid.RowName="firstRow"/>
</CustomControl:MyGrid>
</Window>
It does not display correctly in the designer but works in runtime.

Along the lines of the other answers I came up with this attached property solution that does not require using a custom Grid.
The code is largely redundant (for row & column) and can be used like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="ThisRow"/>
<RowDefinition x:Name="ThatRow"/>
<RowDefinition x:Name="AnotherRow"/>
</Grid.RowDefinitions>
<TextBlock helpers:GridHelper.RowName="ThisRow" Text="..."/>
<TextBlock helpers:GridHelper.RowName="AnotherRow" Text="..."/>
<TextBlock helpers:GridHelper.RowName="ThatRow" Text="..."/>
</Grid>
GridHelper.cs:
public class GridHelper
{
public static string GetRowName(DependencyObject obj)
{
return (string)obj.GetValue(RowNameProperty);
}
public static void SetRowName(DependencyObject obj, string value)
{
obj.SetValue(RowNameProperty, value);
}
public static readonly DependencyProperty RowNameProperty =
DependencyProperty.RegisterAttached("RowName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnRowNamePropertyChanged));
public static void OnRowNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var name = e.NewValue?.ToString();
if (string.IsNullOrEmpty(name)) return;
if (!(sender is FrameworkElement fe)) return;
if (!(fe.Parent is Grid grid)) return;
for (int i = 0; i < grid.RowDefinitions.Count; i++)
{
var rd = grid.RowDefinitions[i];
if (rd.Name.Equals(name))
{
Grid.SetRow(fe, i);
return;
}
}
throw new ArgumentException("Invalid RowName: " + name);
}
public static string GetColumnName(DependencyObject obj)
{
return (string)obj.GetValue(ColumnNameProperty);
}
public static void SetColumnName(DependencyObject obj, string value)
{
obj.SetValue(ColumnNameProperty, value);
}
public static readonly DependencyProperty ColumnNameProperty =
DependencyProperty.RegisterAttached("ColumnName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnColumnNamePropertyChanged));
public static void OnColumnNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var name = e.NewValue?.ToString();
if (string.IsNullOrEmpty(name)) return;
if (!(sender is FrameworkElement fe)) return;
if (!(fe.Parent is Grid grid)) return;
for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
{
var cd = grid.ColumnDefinitions[i];
if (cd.Name.Equals(name))
{
Grid.SetColumn(fe, i);
return;
}
}
throw new ArgumentException("Invalid ColumnName: " + name);
}
}
Note: This also may not work in the designer - I've never tried using it...

Related

Insert user control in Material Design Dialog WPF

I am trying to understand material design in xaml in WPF and at this moment I am working on Dialog Host. I tried to put UserControl in material design dialog host but somehow it is not working, I am using material design with Caliburn Micro FW.
MainView.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--ROW 0-->
<Button Content="Show Dialog" Grid.Row="0" Grid.Column="0" x:Name="OpenDialog"/>
<!--ROW 1-->
<materialDesign:DialogHost Grid.Row="1" Grid.Column="0" IsOpen="{Binding IsDialogOpen}" CloseOnClickAway="True">
<materialDesign:DialogHost.DialogContent>
<StackPanel Margin="20">
<ContentControl x:Name="ActiveItem"/>
</StackPanel>
</materialDesign:DialogHost.DialogContent>
</materialDesign:DialogHost>
</Grid>
MainViewModel.cs
private bool _isDialogOpen;
public bool IsDialogOpen
{
get { return _isDialogOpen; }
set
{
_isDialogOpen = value;
NotifyOfPropertyChange(() => IsDialogOpen);
}
}
public LoginViewModel()
{
MyCustomers.Add("Dhairya Joshi");
MyCustomers.Add("Irfan Shethia");
MyCustomers.Add("Imran Shethia");
}
public void OpenDialog()
{
IsDialogOpen = true;
ActivateItem(new CustomersListViewModel());
}
CustomerView.xaml
<StackPanel Orientation="Vertical">
<ListBox x:Name="MyCustomers"/>
</StackPanel>
CustomerViewModel.cs
private List<string> _myCustomers = new List<string>();
public List<string> MyCustomers
{
get { return _myCustomers; }
set {
_myCustomers = value;
NotifyOfPropertyChange(() => MyCustomers);
}
}
public CustomersListViewModel()
{
MyCustomers.Add("Dhairya Joshi");
MyCustomers.Add("Irfan Shethia");
MyCustomers.Add("Imran Shethia");
}
Right now it is just showing like this screen shot
.
NOTE : I tried to put the <contentControl> in new row and same code does works fine but it is just not showing when i am using it inside DialogHost. Tried to remove StackPanel also and left only with <contentcontrol> but still its not working.
Update:
Finally I was able to put it in working condition but again another problem arises is that ListBox which is inside User Control is not getting populated.
How I was able to show User Control in Dialog:
1) MainView.xaml
<materialDesign:DialogHost Grid.Row="2" Grid.Column="0" IsOpen="{Binding IsDialogOpen}" CloseOnClickAway="True" Identifier="RootDialog">
2) MainViewModel.cs
public async void OpenDialog()
{
//IsDialogOpen = true;
var view = new CustomersListView
{
DataContext = new CustomerListViewModel();
};
//show the dialog
var result = await DialogHost.Show(view, "RootDialog", ExtendedOpenedEventHandler, ExtendedClosingEventHandler);
}
private void ExtendedOpenedEventHandler(object sender, DialogOpenedEventArgs eventargs)
{
Console.WriteLine("Detecting Opened Event");
}
private void ExtendedClosingEventHandler(object sender, DialogClosingEventArgs eventArgs)
{
}
If i run the usercontrol directly then listview populates but when i do it inside dialog it does not. Not sure why.
it's work very good
https://github.com/bebenins/DialogRefreshIssue
good lock

How to Register an Attached Property of Textbox Text So Datagrid Can Update on Change

I have a textbox and a datagrid like so:
<Page
TextElement.FontSize="14" FontFamily="Segoe UI"
Title="Delivery View">
<Page.Resources>
<xcdg:DataGridCollectionViewSource x:Key="firstNameDataSource"
Source="{Binding Path=Accessor.Views[FirstNameView].SourceCollection}"
AutoFilterMode="And"
DistinctValuesConstraint="Filtered">
<xcdg:DataGridCollectionViewSource.ItemProperties>
<xcdg:DataGridItemProperty Name="FirstName" CalculateDistinctValues="False"/>
</xcdg:DataGridCollectionViewSource.ItemProperties>
</xcdg:DataGridCollectionViewSource>
</Page.Resources>
<ScrollViewer Name="pendingScroll" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<DockPanel Name="pnlMainPanel" LastChildFill="True" Style="{StaticResource panelBackground}">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="18" Text="Pending Guests" Margin="0,1,3,1" Foreground="SteelBlue" HorizontalAlignment="Left"/>
<TextBox Name="txtFirstNameFilter" Grid.Row="1" >
</TextBox>
<xcdg:DataGridControl x:Name="gridPendingGuests" Margin="5,0,5,1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MinHeight="100"
MinWidth="200"
CellEditorDisplayConditions="None"
EditTriggers="None"
ItemScrollingBehavior="Immediate"
AutoCreateColumns="False"
SelectionMode="Single"
NavigationBehavior="RowOnly"
ItemsSource="{Binding Source={StaticResource firstNameDataSource}}">
<xcdg:DataGridControl.View>
<xcdg:TableView ShowRowSelectorPane="False"/>
</xcdg:DataGridControl.View>
<xcdg:DataGridControl.Columns>
<xcdg:Column x:Name="FirstName" FieldName="FirstName" Title="First Name" Width="150" />
</xcdg:DataGridControl.Columns>
<i:Interaction.Behaviors>
<utils:UpdateDataGridOnTextboxChange/>
</i:Interaction.Behaviors>
</xcdg:DataGridControl>
</Grid>
</DockPanel>
</ScrollViewer>
</Page>
In the datagrid, you have a collection of first names. This works perfectly. The display is good. As you can see, I added an Interactions.Behavior class which currently handles a filter with a hard coded value when the user clicks on the datagrid with their mouse. The filtering works fine. If there is a first name of "John", that record is removed from view, leaving all other records in place.
Here is that code:
using System.Windows.Interactivity;
using System.Windows;
using Xceed.Wpf.DataGrid;
using System;
namespace Some.Namespace.Behaviors
{
public class UpdateDataGridOnTextboxChange : Behavior<DataGridControl>
{
protected override void OnAttached()
{
AssociatedObject.MouseUp += AssociatedObjectOnMouseUp;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.MouseUp -= AssociatedObjectOnMouseUp;
base.OnDetaching();
}
private void AssociatedObjectOnMouseUp(object sender, RoutedEventArgs routedEventArgs)
{
var items = AssociatedObject.Items;
items.Filter = CollectionFilter;
}
private bool CollectionFilter(object item)
{
System.Data.DataRow dr = item as System.Data.DataRow;
//set the ItemArray as Guest
Guest guest = SetGuest(dr);
if (guest.FirstName.Equals("John"))
{
return false;
}
return true;
}
private Guest SetGuest(System.Data.DataRow dr)
{
Guest guest = new Guest();
guest.FirstName = dr.ItemArray[0].ToString();
return guest;
}
public class Guest
{
public string FirstName { get; set; }
}
}
}
This works as expected. Again, when the user clicks on the datagrid, the filter filters out the users with the First Name of "John".
What I WANT to have happen is for the user to be able to type a first name in the txtFirstNameFilter Textbox and the datagrid to then filter the records that contain the text in the first name, keeping them visible and the others without that first name to not be visible.
The way I can do it is with an attached property of the Textbox TextChanged property? That's a question, because I don't know how to do an attached property and then how to make sure that when that attached property actually changes, call the AssociatedObjectOnMouseUp method to run the filtering.
System.Windows.Interactivity.Behavior<T> inherits from DependencyObject. So give it a dependency property and bind that.
public class UpdateDataGridOnTextboxChange : Behavior<DataGrid>
{
#region FilterValue Property
public String FilterValue
{
get { return (String)GetValue(FilterValueProperty); }
set { SetValue(FilterValueProperty, value); }
}
public static readonly DependencyProperty FilterValueProperty =
DependencyProperty.Register(nameof(FilterValue), typeof(String), typeof(UpdateDataGridOnTextboxChange),
new FrameworkPropertyMetadata(null, FilterValue_PropertyChanged));
protected static void FilterValue_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as UpdateDataGridOnTextboxChange).OnFilterValueChanged(e.OldValue);
}
private void OnFilterValueChanged(object oldValue)
{
// Do whatever you do to update the filter
// I did a trace just for testing.
System.Diagnostics.Trace.WriteLine($"Filter value changed from '{oldValue}' to '{FilterValue}'");
}
#endregion FilterValue Property
/*****************************************
All your code here
*****************************************/
}
XAML:
<i:Interaction.Behaviors>
<utils:UpdateDataGridOnTextboxChange
FilterValue="{Binding Text, ElementName=txtFirstNameFilter}"
/>
</i:Interaction.Behaviors>
You should rename it, though. It's got nothing to do with text boxes. You could bind FilterValue to a viewmodel property, or the selected value in a ComboBox, or whatever.
Update
OP's having trouble with the binding only updating FilterValue when the text box loses focus. This isn't what I'm seeing, but I don't know what's different between the two.
There isn't any UpdateTargetTrigger property of Binding, but you can swap the source and the target when both are dependency properties of dependency objects. This works for me:
<TextBox
x:Name="txtFirstNameFilter"
Text="{Binding FilterValue, ElementName=DataGridFilterThing, UpdateSourceTrigger=PropertyChanged}"
/>
<!-- snip snip snip -->
<i:Interaction.Behaviors>
<local:UpdateDataGridOnTextboxChange
x:Name="DataGridFilterThing"
/>
</i:Interaction.Behaviors>

How to Hide/enable a Column in userControl when calling that control in other window?

I have a User control. It has Some textboxes. I need to hide a single column in that control and if require I need to set as visible. It's like, setting visibility property to a textbox visible/hidden/collapsed. Same thing I need to do in a Column for a user control.
Here is my code.
UserControl Xaml:
<UserControl x:Class="UserControls.UserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions >
<RowDefinition Height="45"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<TextBox Name="txt1"
Text=""
Grid.Row="0" Grid.Column="0"/>
<TextBox Name="txt1"
Text=""
Grid.Row="0" Grid.Column="1"/>
//....some othr controls..
</Grid>
</UserControl>
Window1.cs:
public partial class Window1
{
public Window1()
{
InitializeComponent();
var uc = new UserControl();
grid1.RowDefinitions.Add(new RowDefinition());
Grid.SetRow(uc, grid1.RowDefinitions.Count - 1);
grid1.Children.Add(uc);
}
}
I need to hide the Column 1. How might I conceal this column and if require I need to Enable this column too. Any offer assistance??
Column cannot be hidden by itself. Nevertheless you can wrap elements in column to one panel and then set its visibility to hidden/collapsed. You can also select all UIelements from column and subsequently set their visibility
var elements = Grid.Children.OfType<FrameworkElement>().Where(x => Grid.GetColumn(x) == ColumnNumber).ToList();
elements.ForEach(x => x.Visibility = System.Windows.Visibility.Collapsed);
EDIT
I created Attached Properties for you problem. I have simple grid panel with 2 elements inside
<Grid local:MyGrid.IsHidden="True" local:MyGrid.ColumnNumber="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="1" Grid.Column="0"/>
<TextBlock Text="2" Grid.Column="1" Name="tekst"/>
</Grid>
class MyGrid looks as follows:
public class MyGrid
{
public static void SetIsHidden(DependencyObject obj, bool val)
{
obj.SetValue(IsHiddenProperty, val);
}
public static bool GetIsHidden(DependencyObject obj)
{
return (bool)obj.GetValue(IsHiddenProperty);
}
public static void SetColumnNumber(DependencyObject obj, int val)
{
obj.SetValue(ColumnNumberProperty, val);
}
public static int GetColumnNumber(DependencyObject obj)
{
return (int)obj.GetValue(ColumnNumberProperty);
}
public static readonly DependencyProperty IsHiddenProperty = DependencyProperty.RegisterAttached("IsHidden", typeof(bool), typeof(MyGrid));
public static readonly DependencyProperty ColumnNumberProperty = DependencyProperty.RegisterAttached("ColumnNumber", typeof(int), typeof(MyGrid),
new FrameworkPropertyMetadata(-1, new PropertyChangedCallback((x, y) =>
{
if (x is Grid && GetIsHidden(x))
((Grid)x).Loaded += MyGrid_Loaded;
})));
static void MyGrid_Loaded(object sender, RoutedEventArgs e)
{
if (GetColumnNumber((DependencyObject)sender) >= 0 && GetColumnNumber((DependencyObject)sender) <= ((Grid)sender).ColumnDefinitions.Count - 1)
{
var elements = ((Grid)sender).Children.OfType<FrameworkElement>().Where(z => Grid.GetColumn(z) == GetColumnNumber((DependencyObject)sender)).ToList();
elements.ForEach(s => s.Visibility = System.Windows.Visibility.Collapsed);
}
}
}
Now if you set local:MyGrid.IsHidden to True and insert valid ColumnNumber local:MyGrid.ColumnNumber all UI elements will be hidden.
Standard view
If you set
local:MyGrid.IsHidden="True" local:MyGrid.ColumnNumber="0"
For settings
local:MyGrid.IsHidden="False" local:MyGrid.ColumnNumber="0"
everyting stays normal
As you can see from the ColumnDefinition Class page on MSDN, there is no Visibility property. Therefore, it is not possible to set the Visibililty of a Grid.Column. The customary way to do this would be to set the Visibility on each of the controls in that column.
The standard way to set the Visibility on one or more controls from other controls is to provide one or more bool properties:
public bool IsVisible { get; set; } // Implement INotifyPropertychanged interface here
Then we can data bind that property to the Control.Visibillity property of the relevant controls:
<TextBox Name="txt1" Text="" Grid.Row="0" Grid.Column="0" Visibillity="{
Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
You can use the same Binding on each of the controls in the column, so that they can all be hidden at once:
// Hide column controls
IsVisible = false;
// Show column controls
IsVisible = true;
Finally, if you create a DependencyProperty for the IsVisible property, then you would be able to data bind to it from outside of your UserControl:
<Local:YourUserControl IsVisible="{Binding IsVisibleInMainWindowDataContext}" />
Then from object set as MainWindow.DataContext:
// Hide column controls
IsVisibleInMainWindowDataContext = false;
// Show column controls
IsVisibleInMainWindowDataContext = true;

How can I "stick" a control on top edge of screen?

I have a custom control which shows some statistic data and need always be placed at the top edge of WP7 screen. but, when user inputs something on a textbox, the soft-keyboard popup. And the custom control is moved out of the screen. I want to make sure the custom control always visible, even when soft keyboard is popup. Does anyone know how to do this?
You must to use some "magic". By "magic" I mean RenderTransform.
Solution is simple - you need to move your custom control (down, when keyboard visible; up, when hidden). Check this valuable post - it's must help you.
Regards.
<phone:PhoneApplicationPage
x:Class="Test.Keyboard.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape"
>
<Grid x:Name="LayoutRoot" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="WINDOWS PHONE" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="developer's ?" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid Grid.Row="1" Margin="12,0,12,0"></Grid>
<TextBox Grid.Row="2" LostFocus="TextBoxLostFocus"/>
</Grid>
</phone:PhoneApplicationPage>
public partial class MainPage : PhoneApplicationPage
{
private const double LandscapeShift = -259d;
private const double LandscapeShiftWithBar = -328d;
private const double Epsilon = 0.00000001d;
private const double PortraitShift = -339d;
private const double PortraitShiftWithBar = -408d;
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register("TranslateY", typeof(double), typeof(MainPage), new PropertyMetadata(0d, OnRenderXPropertyChanged));
public MainPage()
{
InitializeComponent();
Loaded += MainPageLoaded;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
private static void OnRenderXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainPage)d).UpdateTopMargin((double)e.NewValue);
}
private void MainPageLoaded(object sender, RoutedEventArgs e)
{
BindToKeyboardFocus();
}
private void BindToKeyboardFocus()
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
var group = frame.RenderTransform as TransformGroup;
if (group != null)
{
var translate = group.Children[0] as TranslateTransform;
var translateYBinding = new Binding("Y");
translateYBinding.Source = translate;
SetBinding(TranslateYProperty, translateYBinding);
}
}
}
private void UpdateTopMargin(double translateY)
{
if (IsClose(translateY, LandscapeShift) || IsClose(translateY, PortraitShift)
||IsClose(translateY, LandscapeShiftWithBar) || IsClose(translateY, PortraitShiftWithBar)
)
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
}
private bool IsClose(double a, double b)
{
return Math.Abs(a - b) < Epsilon;
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
LayoutRoot.Margin = new Thickness();
}
}
Good Luck....

WPF Multi-TextBoxes User Control

I want to have a user control[say UC1] comprising 4 text boxes [say tb1,tb2,tb3, and tb4]. This user control should have 4 normal properties [say prop1, prop2, prop3, and prop4] binding to these text boxes. I want a dependency property [say dp] exposed to outer world by this user control.
This user control gets a single string [say 0\abc|1\def|2\ghi|3\jkl] from a property[say StrProp] of class [say C1] and is splitted into 4 parts[say abc, def, ghi, and jkl] to display in 4 text boxes of my user control. If any changes done by user in any or all textboxes, all the changed texts should be combined and reflected back to class C1\StrProp property.
Also, my requirement is that dp should be bounded to StrProp in UI\XAML. Validations should also be done properly.
Can anyone please help me by writing an example?
Sample classes are as below:
MyMultiTextBoxUserControl.xaml
<UserControl x:Class="MyMultiTextBoxControl_UsingNConsuming.MyMultiTextBoxUserControl"
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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding ElementName=UserControl, Path=CombinedField1 }"/>
<TextBox Grid.Row="1" Text="{Binding ElementName=UserControl, Path=CombinedField2}"/>
<TextBox Grid.Row="2" Text="{Binding ElementName=UserControl, Path=CombinedField3}"/>
<TextBox Grid.Row="3" Text="{Binding ElementName=UserControl, Path=CombinedField4}"/>
</Grid>
</UserControl>
MyMultiTextBoxUserControl.xaml.cs
public partial class MyMultiTextBoxUserControl : UserControl
{
public MyMultiTextBoxUserControl()
{
InitializeComponent();
}
//static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
// FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback), new CoerceValueCallback(CoerceValue_Callback),
// false, UpdateSourceTrigger.LostFocus);
//public static readonly DependencyProperty CombinedTextProperty =
// DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata, new ValidateValueCallback(Validate_ValueCallback));
static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback));
public static readonly DependencyProperty CombinedTextProperty =
DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata);
private static bool Validate_ValueCallback(object value)
{
string str=value as string;
bool result = true;
if (str.Length > 28)
result = false;
if (str.Length < 1)
result = false;
if (str.Substring(0, 2) != "0'\'")
result = false;
if (str.Contains("1'\'") == false || str.Contains("2'\'") || str.Contains("3'\'"))
result = false;
return result;
}
private static object CoerceValue_Callback(DependencyObject obj,object value)
{
return value;
}
private static void PropertyChanged_Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyMultiTextBoxUserControl control=(MyMultiTextBoxUserControl)obj;
string select = e.NewValue.ToString();
char[] pipeDelim,slashDelim;
string[] pipeSplt;
pipeDelim = new char[] { '|' };
slashDelim = new Char[] { '/' };
pipeSplt = select.Split(pipeDelim);
if (pipeSplt.Length == 1)
return;
string[][] str = new string[4][];
int x = 0;
foreach (string s in pipeSplt)
{
if (string.IsNullOrEmpty(s) == false)
{
str[x] = s.Split(slashDelim);
x++;
}
}
control.CombinedField1 = str[0][1];
control.CombinedField2 = str[1][1];
control.CombinedField3 = str[2][1];
control.CombinedField4 = str[3][1];
}
public string CombinedText
{
get { return GetValue(CombinedTextProperty) as string; }
set { SetValue(CombinedTextProperty, value); }
}
public string CombinedField1
{
get; set;
}
public string CombinedField2
{
get;
set;
}
public string CombinedField3
{
get;
set;
}
public string CombinedField4
{
get;
set;
}
}
CombinedStringClass.cs
namespace MyMultiTextBoxControl_UsingNConsuming
{
public class CombinedStringClass
{
public CombinedStringClass() { }
string m_CombinedString;
public string CombinedString
{
get { return m_CombinedString; }
set
{
if (m_CombinedString != value)
m_CombinedString = value;
}
}
}
}
ConsumerClass.xaml
<Window x:Class="MyMultiTextBoxControl_UsingNConsuming.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyMultiTextBoxControl_UsingNConsuming;assembly="
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:CombinedStringClass x:Key="myClass"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.33*"/>
<RowDefinition Height="0.34*"/>
<RowDefinition Height="0.33*"/>
</Grid.RowDefinitions>
<TextBlock Text="User Control Text Boxes" Grid.Row="0" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<local:MyMultiTextBoxUserControl Grid.Row="0" Grid.Column="1" Foreground="Black" CombinedText="{Binding Source=myClass, Path=CombinedString, Mode=TwoWay,FallbackValue=DataNotBound}"/>
<TextBlock Text="Combied String" Grid.Row="2" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBox Name="OneStringTextBox" Grid.Row="2" Grid.Column="1" Foreground="Black" Text="0\abc|1\def|2\ghi|3\jkl" IsEnabled="False"/>
</Grid>
</Window>
I also need to combine the changed texts ofUserControl's textboxes in such a way that it should be in a form of [0\f|1\gh|2\zx|3\oo] to be reflected in OneStringTextBox. Also, total string length should be 28 & max length of each textbox is 7.
Read WPF in C# 2010: Windows Presentation Foundation in .NET 4 Matthew MacDonald Chapter 18.
There is a great example that shoud help you.
Give name to your User control, replace {Binding ElementName=UserControl... with {Binding ElementName=NameOfUserControl, convert CombinedFields properties to DPs.

Resources