I want to divide my window (wpf) in three columns: left column must be DockPanel ( I think StackPanel will not work on Canvas), the right column should be another DockPanel holding a listbox and in the middle I need to have a Canvas.
This is what I have done and I am having problem with left column since it is not expandable. I need the left column as holder of custom object so that user could drag/drop them on canvas. Please advise.
<DockPanel LastChildFill="True" Background="LightGray" Margin="5">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray"
VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
Also, what control should be used on the right side so that can hold a listbox?
Looking at various question you have asked, it looks like you are looking at building an application similar to Visual studio. I would suggest you to look at following great series of articles on CodeProject similar to your requirement i.e. having ToolBox, various ToolBox items , a designer, drag & drop items on designer etc. -
WPF Diagram Designer - Part 4, Part 3, Part 2, Part 1
You will just have to add a PropertyGrid on the right side and bind your selected ToolBox item in designer with it.
You can embed another grid, or canvas, or other kind of container inside of a grid, you don't have to. But you have a GridSplitter so by definition it needs to be placed inside of a Grid in order to provide its functionality, otherwise it doesn't do anything.
It sounds like you want to do something like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDeifnition Width="*" />
<ColumnDefinition Width="100" /> <!-- whatever size you need here --->
</Grid.ColumnDefinitions>
<DockPanel LastChildFill="True" Background="LightGray" Margin="5" Grid.Column="0">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray" Grid.Column="1"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ResizeDirection="Columns"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight" Grid.Column="2"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
<ListBox Grid.Column="3" ... />
</Grid>
This layout gives you your 3 columns, the 2nd column is your splitter that lets you resize the first 2 columns (sliding back and forth between them), and your 3rd column is a fixed size.
Create a Grid with 3 columns. For the 1st and 3rd put a DockPanel as child.
I think the general rule is don't put others under canvas, not the other way round.
Related
So I have a rather interesting question. I have a viewbox that has a few elements in it (a custom user control for an image, a canvas, a label, and a textbox). What I want is to try and have all elements scale with the viewbox, but I want the label and the textbox to have a "Max Size." I have tried using a max width and height on these controls but they seem to ignore it. If someone could take a look at my code below an slap me for what I am doing wrong that would be appreciated.
<Viewbox Name="myViewBox" Stretch="Uniform">
<!--Grid used to track mouse movements in this element for other reasons -->
<Grid Name="grdViewboxGrid" MouseMove="trackMouse">
<Canvas Name="cvsViewboxCanvas" MinWidth="270" MinHeight="270"
VerticalAlignment="Top" HorizontalAlignment="Center"
Panel.ZIndex="1" Background="Black"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove">
<Grid>
<!--Would rather not post here for Intellectual Property reasons-->
<!-- Extension of the image control -->
<CustomImageUserControl />
<Grid>
<Grid Width="{Binding LabelWidthPercentage}"
MaxWidth="50"
Height="{Binding LabelHeightPercentage"
MaxHeight="26"
SnapsToDevicePixels="True" VerticalAlignment="Top"
HorizontalAlignment="Left" Margin="5" IsHitTestVisible="False">
<Label Name="lblViewboxLabel" HorizontalAlignment="Left"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
</Grid>
<Grid>
<Grid Width="{Binding TextBoxWidthPercentage}"
MaxWidth="156"
Height="{Binding TextBoxHeightPercentage}"
MaxHeight="45"
SnapsToDevicePixels="True" Vertical="Top"
HorizontalAlignment="Right" Margin="5" IsHitTestVisible="False">
<Border Style="{DynamicResource CustomBorder}" />
<Grid>
<Textbox Name="txtViewboxTextBox" Text="{Binding txtViewbox}" />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Canvas>
</Grid>
</Viewbox>
If I am not including something that is needed please let me know and I will update my question. Any help would be greatly appreciated this is now day 4 on this issue sadly :-(
I am not sure why you need so many overlapping Grids, but I hope that I can answer your question nevertheless:
In order to have the label left of the text box and to assign a maximum width to each of these two controls, use a Grid with two columns and set the MaxWidth property for each column. Then, assign the label to the left column (the one with index 0) and assign the text box to the right column (index 1). The corresponding code fragment looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="30"/>
<ColumnDefinition MaxWidth="156" MinWidth="30"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" x:Name="lblViewboxLabel" HorizontalAlignment="Left" Foreground="Yellow"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
<TextBox Grid.Column="1" x:Name="txtViewboxTextBox" Text="{Binding txtViewbox}" Background="Orange"/>
</Grid>
I also have assigned a MinWidth to the right column; this is necessary to make sure that the text box does not completely disappear if it contains no text.
I am still trying to get my head around wpf ui creation. I have created a user control which has a expander, dockpanel and two comboboxes in the dockpanel with equal width hardcoded in xaml. It looks good during the design time and in runtime as long as the expander widths is unchanged. When the expander header is changed to a longer text the dockpanel streches and the two comboxes remain the same size. They are docked to the right hence there is a lot of gap between two comboxes.
My question, is there a way to equally space the two comboboxes in the dockpanel.
Code:
<DockPanel Height="Auto"
DockPanel.Dock="Top"
HorizontalAlignment="Stretch" Name="lhsInput"
VerticalAlignment="Stretch" Width="Auto"
LastChildFill="False">
<ComboBox Height="23" Name="cboLHSItem"
Width="84" FontWeight="Normal"
Margin="1,0,0,0" Foreground="Black" FontFamily="Tahoma" />
<ComboBox Height="23" DockPanel.Dock="Right"
Name="cboLHSValues" Width="79" FontWeight="Normal"
IsEditable="False" Margin="0,0,1,0" FontFamily="Tahoma"
Foreground="Black" />
</DockPanel>
You could use Grid instead of DockPanel, Sample code below:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox Height="23" Name="cboLHSItem"
MinWidth="79"
Grid.Column="0"
FontWeight="Normal"
Foreground="Black" FontFamily="Tahoma" />
<ComboBox Height="23" MinWidth="79"
Grid.Column="1"
Name="cboLHSValues" FontWeight="Normal"
IsEditable="False" FontFamily="Tahoma"
Foreground="Black" />
</Grid>
DockPanel may be providing the required Width for its Child elements. However, with Grid and its RowDefinitions and ColumnDefinitions you can specify whether you want "Auto" Width/Height or you want to use whatever is available for Width/Height. The above code specifies to use Whatever width available for the Child element by setting Width to * in ColumnDefinition.
UPDATE
To Understand how different Panels of WPF work, refer to MSDN page: Panels Overview
I cant figure out what I am doing wrong I have a grid with 2 columns and 3 rows. In the left column I have a textblock and textbox and a listbox which is all good.
The right column gets a little more complicated where I have a tabcontrol to start. Then my TabItem and inside that I have my Main Grid and then inside that I have 2 grids. Which are grdDetailsTop and then grdDetailsBottom.
grdDetailsTop has 3 columns where the left will be an image with a Border the middle should be Member Code: 'TextBox' and under that should be Family Code: 'TextBox' and then finally under that I would like to place Balance: 'TextBox'
The way I tried it was, I have grdDetailsTop Grid with 3 columns in the first column I placed a groupbox and inside that I have a stackpanel.
Second Column is where I am having trouble I placed a Stackpanel with the orientation Horizontal and grid.Column="1" but my textblocks are going into the 3rd column without me telling them too. Sorry for the bad explanation but the code is posted hopefully you can help. Also the reason for my different Panels is so I can place a border around each column of the grdDetailsTop.
Thanks
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"></ColumnDefinition>
<ColumnDefinition Width="70*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="5*"></RowDefinition>
<RowDefinition Height="100*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="Search Member" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"></TextBlock>
<TextBlock Text="Member Details" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"></TextBlock>
<TextBox Name="txtMEMSearch" Background="Orange" Grid.Column="0" Grid.Row="1"></TextBox>
<ListBox Name="lstSearchMembers" Grid.Row="2"
BorderBrush="Black" DisplayMemberPath="Name"
ItemsSource="{Binding ElementName=bindingToObject,
Path=Clients}" />
<TabControl Name="mainTabControl" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5">
<TabItem Header="Member Details" Name="memDetailTab">
<Grid Name="mainTabGrid">
<Grid Name="grdDetailsTop" Height="175" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<GroupBox Header="Picture">
<StackPanel>
<Image Height="125" Width="125"></Image>
</StackPanel>
</GroupBox>
*************PROBLEM AREA******************
<StackPanel Margin="5" MaxWidth="200" Orientation="Horizontal" Grid.Column="1">
<TextBlock
VerticalAlignment="Top"
Margin="5"
Height="25">Member Code:</TextBlock>
<TextBlock
VerticalAlignment="Top"
Margin="5"
Width="75"
Height="25"></TextBlock>
<TextBlock
Margin="5"
Height="25"
Width="100">Family Code:</TextBlock>
<TextBlock
Margin="5"
Width="75"
Height="25"></TextBlock>
<TextBlock
VerticalAlignment="Bottom"
Margin="5"
Height="25"
Width="100">Balance Due:</TextBlock>
<TextBlock
Margin="5"
VerticalAlignment="Bottom"
Width="75"
Height="25"></TextBlock>
</StackPanel>
******************************************
</Grid>
<Grid Name="grdDetailsBottom">
</Grid>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Page>
You're missing the VerticalAlignment = "Top" on that TextBlock, and therefore, it is defaulting to Stretch which will center the text in the available vertical space. If you set that property, you'll see that it moves back up to be in line with the other TextBoxes in the same StackPanel.
It isn't actually moving that TextBox into the next column, rather, you've defined a fixed width of the column it is in (in this case, column 1 with a width of 200). But the contents of that column (the StackPanel are more than 200 units wide, so they push over into the next grid column. To keep all the TextBoxes in column 1, you'll either need to widen the column so they can fit, shrink the size of the TextBoxes or set the width Auto so it will automatically size itself to its contents. If you look at it, you're taking up 175 units with TextBlocks 2 and 3, leaving only 25 units for the rest of the TextBlocks in that StackPanel. There's just not enough space.
<StackPanel Margin="5" MaxWidth="200" Orientation="Horizontal" Grid.Column="1">
<TextBlock VerticalAlignment="Top" Margin="5" Height="25">Member Code:</TextBlock>
<TextBlock VerticalAlignment="Top" Margin="5" Width="75" Height="25"></TextBlock>
<TextBlock VerticalAlignment="Top" Margin="5" Height="25" Width="100">Family Code:</TextBlock>
... other text boxes ...
</StackPanel>
This will fix the vertical layout issue, but not the Horizontal layout issue. That requires changes to the grid column sizing or the size of the contents.
Per your comment below, I believe you're looking for the WrapPanel which will automatically wrap items to the next line (horizontal or vertical) when it runs out of space.
<WrapPanel Margin="5" MaxWidth="200" Orientation="Horizontal" Grid.Column="1">
<TextBlock VerticalAlignment="Top" Margin="5" Height="25">Member Code:</TextBlock>
<TextBlock VerticalAlignment="Top" Margin="5" Width="75" Height="25"></TextBlock>
<TextBlock VerticalAlignment="Top" Margin="5" Height="25" Width="100">Family Code:</TextBlock>
... other text boxes ...
</WrapPanel>
This layout certainly looks nicer. But - I don't know your specific requirements, however rather than using a WrapPanel with fixed margins and sizes, I would recommend using a Grid with ColumnDefinitions and RowDefinitions to organize items in this type of layout. The Grid offers a much greater level of flexibility and allows for items to automatically resize based on system font sizes, a user resizing your view and other factors out of your control. If you are setting fixed heights/widths, you lose that flexibility. Again, I don't know you're requirements, so perhaps this is the best solution for you, but under most circumstances, I'd highly recommend a Grid instead.
User control details:
Have created drop down list control (Like as combo box), clicking on down arrow button, it displays a list below the text box
I have set zIndex property of my User control
Issue:
Case 1: When there is another user control (other than my custom user control), and if drop down list is displayed, other user control hides behind my user control. This is perfectly Ok
Case 2: There are 2 Custom User controls, if list is displayed from first user control, second user control appears on the list. This is where i am facing issue
XAML of my control is as below
<UserControlx:Class="UserControls.AutoCompleteComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Panel.ZIndex="1110" LostFocus="UserControl_LostFocus" Height="Auto">
<Canvas Name="MainCanvas">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="150"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Name="autoTextBox" Height="20" MinWidth="150" Width="Auto" MinHeight="20" Style="{DynamicResource AutoCompleteBox}" BorderThickness="2"
Margin="0,0,0,0" TextWrapping="NoWrap" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Button Content="6" FontFamily="Marlett" Grid.Row="0" Grid.Column="1" FontSize="15" Margin="0,0,0,0" Height="20" Width="20" HorizontalAlignment="Right" VerticalAlignment="Top" Background="{StaticResource BackgroudBlueBrush}" Click="Button_Click" Padding="0" Cursor="Hand"></Button>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" >
<ListBox Name="suggestionListBox" SelectionChanged="suggestionListBox_SelectionChanged" MouseDown="suggestionListBox_MouseDown"
Background="LightYellow" SnapsToDevicePixels="True"
Visibility="Collapsed"
MinWidth="150" IsHitTestVisible="True" MinHeight="70" Height="70"
VerticalAlignment="Top" LostFocus="suggestionListBox_LostFocus"/>
</StackPanel>
</Grid>
</Canvas>
</UserControl>
Your approach is not the right one to correctly manage the overlap of controls. Perhaps you may create some trick using the ZIndex property, but that won't be the solution.
If you need a drop-down control, the best way is to use a Popup control and play around it. Basically, it creates another borderless window, being child of the yours.
Another approach, maybe simpler but not good as the Popup, is using an Adorner. Maybe this one is the most similar techinique to the yours.
Cheers
Have you tried setting the ZIndex of the StackPanel to 1+ the control's zindex? That should raise the drop down portion above any other instance of your user control.
Canvas.ZIndex can be used on StackPanels.
In my application I have some controls that logically belongs together and is reused many places in different windows. The controls are always placed inside a grid.
Instead of copying the controls (and the code behind) each time I want to use them, I would like to define and maintain them in a single xaml file as a single UserControl.
I have this now:
<Grid>
<Grid.ColumnDefinitions>
[ColumnDefinitions...]
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
[RowDefinitions...]
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
[More controls..]
</Grid>
But I want to reuse this part:
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
as a single control - but how do I define the Grid.Column when using the control (somehow supplying it as a parameter)? - and how do I set the Grid.RowSpan value (eventhough the code is moved to a new xaml file, and not defined inside a grid)?
Any comments?
Make them into a separate usercontrol, then include that in your project.
If you're using Blend, it's really easy, just select all the controls, right click and Make into Usercontrol.
You could also make it into a resource.
Define it in a ResourceDictionary and include the dictionary the places you want to use it.
There is one catch - the resource dictionary returns the same instance everytime - so you have to add the attribute x:Shared="false".
But the wpf way is to figure out how you can do it with a DataTemplate :)