WPF DataBinding: Grid ColumnDefinition Width to UserControl DependencyProperty - wpf

I'm not sure what I'm doing wrong, but I am attempting to align my user control with the ColumnWidth of my grid using DataBinding and DependencyProperties.
I have a GridView on my Form Page which contains a two instances of a user control (Address). The address usercontrol contains a textlabel "header" so that I can name the user control "Office Address" or "Mailing Address" etc and is formatted using it's own GridView. I added a DependencyProperty to the user control called "HeaderWidth" which is simply an Int32. The problem is that even with the bindings, things don't align. I'm not sure what I'm doing wrong.
AddressField.xaml:
<UserControl x:Class="AddressField" x:Name="PART_AddressFieldControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<StackPanel Orientation="Horizontal">
<Grid x:Name="StandardView">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=PART_AddressFieldControl, Path=HeaderWidth}" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<!-- ETC REMOVED FOR COMPACTNESS -->
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" Text="{Binding ElementName=PART_AddressFieldControl, Path=Header}" />
<TextBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="8" MinWidth="300" Text="{Binding Address1}" />
<!-- ETC REMOVED FOR COMPACTNESS -->
</Grid>
</StackPanel>
</UserControl>
AddressField.cs
public partial class AddressField : UserControl
{
static AddressField()
{
HeaderProperty = DependencyProperty.Register("Header", typeof(String), typeof(AddressField), new UIPropertyMetadata("Address:"));
HeaderWidthProperty = DependencyProperty.Register("HeaderWidth", typeof(Int32), typeof(AddressField), new UIPropertyMetadata());
ViewProperty = DependencyProperty.Register("View", typeof(AddressFieldView), typeof(AddressField), new UIPropertyMetadata(AddressFieldView.STANDARD));
}
public AddressField()
{
InitializeComponent();
}
public String Header
{
get { return (String)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty;
public Int32 HeaderWidth
{
get { return (Int32)GetValue(HeaderWidthProperty); }
set { SetValue(HeaderWidthProperty, value); }
}
public static readonly DependencyProperty HeaderWidthProperty;
}
RegistrationForm.xaml
<!-- ETC REMOVED FOR COMPACTNESS -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" Name="FirstWidth" />
<ColumnDefinition Width="Auto" />
<!-- ... ETC ... -->
</Grid.ColumnDefinitions>
<vws:AddressField Grid.Row="1" Grid.Column="0" Grid.RowSpan="3" Grid.ColumnSpan="9" Header="Office Address:" DataContext="{Binding OfficeAddressVM}" HeaderWidth="{Binding ElementName=FirstWidth, Path=ActualWidth}" />
<vws:AddressField Grid.Row="4" Grid.Column="0" Grid.RowSpan="3" Grid.ColumnSpan="9" Header="Mailing Address:" DataContext="{Binding MailingAddressVM}" HeaderWidth="{Binding ElementName=FirstWidth, Path=ActualWidth}" />
Basically, I'm trying to align the textlabel of the usercontrol with the registrationform first column width.
EDIT:
Oh, and just because I was curious: I put a debugging breakpoint and dug deep into the UI Tree and got to the AddressField's "Header" (TextBlock) and the ActualWidth is not 0.0 but 73.9 or something like that, yet the UI doesn't take this into account, it still is not visible because the width is 0.0.

There are easier ways you know :)
Simply put Grid.IsSharedSizeScope="True" on the grid in your main window (not in the control), and SharedSizeGroup="someRandomName" on all the columns you wish to share width - in this case, only the column you've bound in the user control, i.e. replace the binding with SharedSizeGroup. That's it! Any columns with the same SharedSizeGroup value will now be the same size, which is the size of the largest of them.
Granted, it only works well with Auto sizing but it sounds like that's what you want.
For more information you can look here and here.
Edit: as to what you were doing wrong, I suspect that the outer grid column, being sized to Auto, but not having any contents of its own to size by (because all the controls were spanning multiple columns), took on a width of zero. The bindings then propagated to the user controls. There are a lot of complicated interactions there though, and I don't have the entire XAML to judge, so I might be wrong.

Related

How to add controls dynamically to a UserControl through user's XAML, 4.0

I want to create a user control that contains a Image and a Conainer that will allow the user to add his/her own controls to the user control dynamically in XAML. I am using VS 2010, .NET 4.0
I have created following code.
UserController.xaml
<Grid DockPanel.Dock="bottom">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Height="Auto" Grid.Row="0" Grid.Column="0">
<Image />
</StackPanel>
<Grid>
<ContentControl Content="{Binding Innercontent}" />
</Grid>
</Grid>
ModeViewCode, Innercontent is an DependencyProperty property
public static DependencyProperty InnerContentProperty = DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(Footer))
public UIElement InnerContent
{
get { return (UIElement)GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
Created a DataTemplate which consumes the usercontrol.
<users:Footer HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<users:Footer.InnerContent>
<StackPanel Orientation="Horizontal" x:Name="tst">
<commoncontrols:Button />
</StackPanel>
</users:Footer.InnerContent>
</users:Footer>
when code is complied it gives me following error message.
Error 275 Unknown build error, 'Index (zero based) must be greater than or equal to zero and less than the size of the argument list. Line 2582 Position 23.' at this line (users:Footer.InnerContent).
Got help from following psot Help
Any help will be appreciated.

How to I make an Expander whose expanded size can be controlled via a GridSplitter? [duplicate]

I would like to have something like a resizable Expander. My basic idea was something like this:
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Expander Grid.Column="0" ExpandDirection="Right">
...
</Expander>
<GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
...
</Grid>
The problem with this: if i move the grid splitter and collaps the expander i got a big empty area. How can make the entire column collapse? Or is there another way to make the expander "resizable"
Not sure what you are trying to accomplish but i think conceptually the Grid should be part of the Expander.Content, would this work for you?
<Expander Header="Test" ExpandDirection="Right" HorizontalAlignment="Left" Background="LightBlue">
<Expander.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Lorem ipsum dolor sit"/>
<GridSplitter Grid.Column="1" Width="5" ResizeBehavior="PreviousAndCurrent" ResizeDirection="Columns"/>
</Grid>
</Expander.Content>
</Expander>
Edit: Removed all the triggering from the first column as it seemed unnecessary.
Also: For this to work vertically the GridSplitter's HorizontalAlignment must be set to Stretch, otherwise it will have zero width by default (of course everything else that is orientation-specific must be adapted as well but that is straightforward)
HorizontalAlignment is the Microsoft .NET property accessor for what is in reality a dependency property. This particular dependency property quite frequently has its apparent "default" value set differently in subclassed elements, particularly controls. [...] For example, the apparent "default" of HorizontalAlignment for a Label control will be Left, even though Label inherits HorizontalAlignment direct from FrameworkElement. This is because that value was reset within the default style of Label, within the style's control template.
Maybe this will help to solve your "column collapse" problem
XAML:
Add in <Grid> Name="expGrid" and add in <Expander> Collapsed="Expander_Collapsed"
C# Code:
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
var colomnIndex = Grid.GetColumn(sender as Expander);
var colomn = expGrid.ColumnDefinitions[colomnIndex];
colomn.Width = GridLength.Auto;
}

How to add a WPF grid control from its XAML string at runtime?

Let's say we have a grid XAML like below - eg. a generated string returned from a method.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width='*' />
<ColumnDefinition Width='*' />
</Grid.ColumnDefinitions>
<TextBlock Text='id' Grid.Column='0'/>
<Rectangle Fill='Black' Grid.Column='1' />
</Grid>
What I want to do is to create such a grid and added to a stackpanel at run time, codes similar as below.
XmlReader xr = XmlReader.Create(input: new StringReader(g.xaml));
var control = XamlReader.Load(xr) as Grid;
this.stackPanel.Children.Add(control);
The form I use is:
<Window x:Class='AllRibbonBrushes.MainWindow'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='MainWindow' Height='223' Width='533'
Loaded='Window_Loaded'>
<ScrollViewer>
<StackPanel Name="stackPanel">
<!--The runtime grid need to be added here-->
</StackPanel>
</ScrollViewer>
</Window>
But I get the error Cannot create unknow type 'Grid'. I succeed doing this by adding a button/a textblock but failed to add a grid with nested controls.
If you know how to do so, please share. All helps are welcome and very much appreciated!
Add xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' to the first Grid element in the xaml you would like load. This declares the wpf namespace the default namespace in your xaml. XamlReader.Load can then find out what kind of control is.
<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width='*' />
<ColumnDefinition Width='*' />
</Grid.ColumnDefinitions>
<TextBlock Text='id' Grid.Column='0'/>
<Rectangle Fill='Black' Grid.Column='1' />
</Grid>

WPF Data Binding - Data Template is not Getting Values

I have the following DataTemplate:
<DataTemplate DataType="{x:Type Client:WorkItem}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Label Name="lblIDText" Margin="2">WI ID:</Label>
<Label Name="lblID" Margin="2" Grid.Column="1"
Target="{Binding Id}"></Label>
<Label Name="lblTitleText" Grid.Row="1" Margin="2">WI Title:</Label>
<Label Name="lblTitle" Margin="2" Grid.Row="2" Grid.ColumnSpan="2"
Target="{Binding Title}"></Label>
</Grid>
</DataTemplate>
in my <Window.Resources> section. It is meant to show the Id and Title of a WorkItem object (from namespace Microsoft.TeamFoundation.WorkItemTracking.Client.)
I try to put this in a TabItem in a TabControl and it only shows the static text. (The WorkItem ID and title do not show, but the static text in my template does.)
Clearly the template is being fired, but the binding is not working and I can't seem to see why.
Here is my C# that calls it:
private void PickWorkItem_Click(object sender, RoutedEventArgs e)
{
int Id = int.Parse(((Button) e.OriginalSource).Tag.ToString());
_mediator.PickedWorkItem = GetWorkItemInQueryResultListByID(Id);
tabAddLinks.DataContext = _mediator.PickedWorkItem;
tabAddLinks.Content = _mediator.PickedWorkItem;
}
I tried it with DataContext in and out and it works the same. When I debug, the value for _mediator.PickedWorkItem is set correctly (Id and Title are both fine).
Any ideas on how to fix this would be appreciated.
You're binding the Label's Target property, when you actually want to be binding it's Content property:
<Label Content="{Binding Id}"/>
Also, consider using a TextBlock instead of a Label if you don't need the extra functionality a Label provides:
<TextBlock Text="{Binding Id}"/>
You are binding Label.Target. Target is the UIElement being labelled. You need to bind Label.Content, or change it to a TextBlock and bind TextBlock.Text.
(My guess would be that you were trying to bind a non-existent Label.Text property and Intellisense helpfully chose Target for you instead...!)
I'm pretty new to WPF, so excuse me if I'm off base, but don't you need to set the Content property of the label, not the Target?

Allow TextBox to be resized but not to grow on user input

I have a TextBox defined inside a window like so:
<Window x:Class="NS.MainWindow"
...
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow" />
</Grid>
</Window>
The problem is that when the user types in the TextBox it expands to the right since only the MinWidth is set. What I really want is the text to wrap to the next line. I can get it to do this if I change the MinWidth on the column to be Width instead. However if I do this, then the TextBox no longer resizes when the Window is resized.
Is there a way I can have both? (i.e. resize only on Window resize, otherwise wrap)
The reason you're having this behavior is because you've set the Window's SizeToContent property - which basically authorizes the Window to resize itself based on the size requested by its content. So as you type in more stuff, the textbox says I need more space, the window obediently grows. Your textbox would not grow if you don't set the SizeToContent property.
So I'd say lose the SizeToContent property setter & Use proportional grid sizing. Here I say make Column#2 twice the width of Column#1. The default "Stretch" value of HorizontalAlignment and VerticalAlignment for the Grid should ensure that your controls resize correctly on a window resize.
<Window ...
Title="MyWindow" WindowStyle="ToolWindow" ResizeMode="CanResizeWithGrip"
MinWidth="300" Width="300" Height="80">
<Grid x:Name="myGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100"/>
<ColumnDefinition Width="2*" MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow"/>
</Grid>
If you just add the SizeToContent property setter back to above code snippet... you'd see some weird behavior where the textbox initially grows with text content.. however if you resize the window once.. the textbox would stop growing. Strange... can't explain that behavior.
HTH
WPF's TextBox doesn't seem to have that option built-in.
To solve this problem, you can use a custom TextBox that reports a desired (0, 0) size. It's an ugly hack, but it works.
In your MainWindow.xaml.cs file:
namespace NS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
...
}
// Ugly HACK because the regular TextBox doesn't allow autoresize to fit the parent but NOT autoresize when the text doesn't fit.
public class TextBoxThatDoesntResizeWithText : TextBox
{
protected override Size MeasureOverride(Size constraint)
{
return new Size(0, 0);
}
}
}
Then, in your MainWindow.xaml file:
<Window x:Class="NS.MainWindow"
...
xmlns:local="clr-namespace:NS"
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<local:TextBoxThatDoesntResizeWithText Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow" />
</Grid>
</Window>
Change the second ColumnDefinition to be Width="*".

Resources