Simple question. I've subclassed StackPanel as you can see below. I'm trying to change it's default for Grid.IsSharedSizeScopeProperty to true since the default behavior should have that set.
As you can see, I tried overriding the property in the static initializer, but it's not reflecting in the UI. However, if I instead explicitly set it in the instance constructor, it works!
My question is why is the default being overridden not affecting the layout but the explicit setting of it does?
Here's my XAML...
<c:LabeledStackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Short: " TextAlignment="Right" />
<TextBox Grid.Column="1" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Medium-size: " TextAlignment="Right" />
<TextBox Grid.Column="1" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Really long size: " TextAlignment="Right" />
<TextBox Grid.Column="1" />
</Grid>
</c:LabeledStackPanel>
Trying to change the default for the property. This does not work...
public class LabeledStackPanel : StackPanel {
static LabeledStackPanel(){
Grid.IsSharedSizeScopeProperty.OverrideMetadata(
typeof(LabeledStackPanel),
new FrameworkPropertyMetadata(true));
}
}
But this does (so I know the property itself properly affects the layout):
public class LabeledStackPanel : StackPanel {
public LabeledStackPanel(){
Grid.SetIsSharedSizeScope(this, true);
}
}
Ironically, doing both doesn't work either! This one really has me stumped! It's as if trying to set the default somehow breaks the behavior entirely.
public class LabeledStackPanel : StackPanel {
static LabeledStackPanel(){
Grid.IsSharedSizeScopeProperty.OverrideMetadata(
typeof(LabeledStackPanel),
new FrameworkPropertyMetadata(true));
}
public LabeledStackPanel(){
Grid.SetIsSharedSizeScope(this, true);
}
}
So what am I missing here?
My guess is it may have something to do with the following comment from here (although this isn't a template):
Grid size sharing does not work if IsSharedSizeScope is set to true within a resource template, and a SharedSizeGroup is defined outside of that template.
For the record, I have also tried using AddOwner to redefine the property that way, but same thing. Doesn't work.
Related
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.
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;
}
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>
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.
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="*".