Stackpanel Visibility - what am I doing wrong . . - wpf

I want to make a ListBox containing StackPanels as its elements. The StackPanels will be created and added at runtime, in the C# code behind. .
The StackPanels will contain some images but at the moment none of the image stuff exists yet, so in this code I just wanted to make sure I could do the mechanics.
My XAML looks like this:
<Grid>
<ListBox Name="listBoxImages" BorderBrush="DarkGray" Width="600" Height="300" BorderThickness="3"
Margin="0" Padding="0" Background="#FFC0C0C0"/>
</Grid>
In the C# code-behind I deliberately set a background color of the Listbox different from the one in the XAML to verify I was accessing the ListBox properly in the code-behind.
listBoxImages.Background = Brushes.Blue; //just to show I'm accessing it . . .
That part works; the ListBox displays blue.
Then I went to add a StackPanel. Since there's nothing in it yet I gave it a height and width and a different background color, but I don't see anything. So I checked its visibility and it's false. So I tried setting the visibility using System.Windows.Visibility.Visible but it's still false after that.
StackPanel myStackPanel = new StackPanel();
myStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
myStackPanel.VerticalAlignment = VerticalAlignment.Top;
myStackPanel.Background = Brushes.Bisque; // make something visible
myStackPanel.MinHeight = 50;
myStackPanel.Width = 50;
bool bResult = myStackPanel.IsVisible;
myStackPanel.Visibility = System.Windows.Visibility.Visible;
bResult = myStackPanel.IsVisible;
myStackPanel.Margin = new Thickness(10);
listBoxImages.Items.Add(myStackPanel);
Why is the StackPanel visibility false and is that the reason why I don't see it after adding it to the ListBox? (I'm sorry if this is a noob question)

IsVisible is set to true when it gets rendered on UI.
You can verify by hooking to Loaded event and see value of IsVisible in it by putting breakpoint on the handler.
myStackPanel.Loaded += (s, e) => bResult = myStackPanel.IsVisible;
Also, I verified with your posted code and can see StackPanel rendered on UI.
More verbose definition:
.........
listBoxImages.Items.Add(myStackPanel);
myStackPanel.Loaded += new RoutedEventHandler(myStackPanel_Loaded);
}
void myStackPanel_Loaded(object sender, RoutedEventArgs e)
{
bool isVisible = (sender as StackPanel).IsVisible;
}

Listbox is better populated with an items template. If you want to add arbitrary controls of different types, just use a stack panel.

Related

What is wrong with Canvas.SetTop(element,position)

I am trying to move and reposition elements with mouse and through code. But I guess I'm missing something or do something the wrong way. So I built a little sample app. It's just a empty wpf app with this MainWindow function
public MainWindow()
{
InitializeComponent();
Label lText = new Label();
lText.Content = "this is my test label";
lText.Height = 50;
lText.Width = 50;
lText.Background = Brushes.Aqua;
// do I really need to do this?
lText.VerticalAlignment = System.Windows.VerticalAlignment.Top;
lText.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
// this part already fails
Canvas.SetTop(lText, 20);
Canvas.SetLeft(lText, 10);
this.Content = lText;
}
The attached properties Canvas.Left and Canvas.Top (which in code behind are set by the Canvas.SetLeft and Canvas.SetTop methods) only have an effect for an element that is a direct child of a Canvas control.
So declare a Canvas as Content element in your MainWindow's XAML:
<Window ...>
<Canvas x:Name="canvas" />
</Window>
Then add the element to the Canvas' Children collection in code behind:
Canvas.SetTop(lText, 20);
Canvas.SetLeft(lText, 10);
canvas.Children.Add(lText);
The solution was that the Left and Top properties must be set inside the Xaml before you can read it. I was always getting NaN and therefor couldn't set the correct value either.

Window sizing constraints by content

I want the window to respect MinWidth/MinHeight and MaxWidth/MaxHeight specifications of the content control inside.
Some suggested using SizeToContent, but this only helps to set the initial window size, not the constraints.
Others suggested overriding MeasureOverride and setting window's Min/Max height and width there, but this seems to be somewhat unclean, considering that such a trivial problem should surely have a purely declarative solution.
Just to mention another solution which seems reasonable but does not work (and had been previously mentioned in an answer which got deleted): binding MinWidth of the window to MinWidth of the control does not take into account window decorations.
If the initial window size is set so that actual content size is not coerced by the content's MinWidth/MinHeight and MaxWidth/MaxHeight in the initial layout pass (for example, by using Window.SizeToContent="WidthAndHeight"), then following equations are true:
Window.ActualSize - Content.ActualSize =
Window.MinSize - Content.MinSize = Window.MaxSize - Content.MaxSize.
Based on these equations you can derive the following code:
public MainWindow()
{
InitializeComponent();
this.SizeChanged += OnWindowSizeChanged;
}
private static void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
var window = (Window)sender;
var content = (FrameworkElement)window.Content;
window.MinWidth = window.ActualWidth - content.ActualWidth + content.MinWidth;
window.MaxWidth = window.ActualWidth - content.ActualWidth + content.MaxWidth;
window.MinHeight = window.ActualHeight - content.ActualHeight + content.MinHeight;
window.MaxHeight = window.ActualHeight - content.ActualHeight + content.MaxHeight;
window.SizeChanged -= OnWindowSizeChanged;
}
I do not know how to achieve this efficiently using the pure declarative approach since the code should be ran just once after the initial layout pass.
Some suggested using SizeToContent, but this only helps to set the initial window size, not the constraints.
I worked around this by setting the MinWidth and MinHeight properties right after the windows was initialized:
MainWindow.xaml
<Window ... SizeToContent="WidthAndHeight">
...
</Window>
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
SourceInitialized += (s, e) =>
{
MinWidth = ActualWidth;
MinHeight = ActualHeight;
};
}
Use Binding markup extension. A binding is wpf's way of saying when this property (source) changes update some other property (target). In this case the Grid's MinWidth property is the Source and your window's MinWidth property is the target.
<Window x:Class="MinMaxValuesOnWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="800"
MinWidth="{Binding ElementName=gridy, Path=MinWidth}"
MinHeight="{Binding ElementName=gridy, Path=MinHeight}"
MaxWidth="{Binding ElementName=gridy, Path=MaxWidth}"
MaxHeight="{Binding ElementName=gridy, Path=MaxHeight}">
<Grid Name="gridy" MinHeight="80" MinWidth="80" MaxHeight="300" MaxWidth="300"/>
</Window>
As you mentioned in the topic this does not completely work, but you can use a converter on the binding to add on the window frame's height and width before updating the binding target (might require a PInvoke). Since I doubt the window frame thickness is dynamically changing in your application this can probably just be constant value (not necessarily true if user changes themes).

Programmatically set content in Silverlight DataGrid details

I need to dynamically set the contents within the template of a DataGrid based on information in an external settings file. That settings file specifies which data fields should display in the DataGrid. The administrator of the application can edit the settings to change the fields to display. I cannot hard-code the fields to display.
I can easily add the columns (DataGridTextColumn's) to the DataGrid at runtime. I set a binding to a field in the item source based on the settings, and that displays fine.
Now I need to display details when the user clicks a row. I set up a RowDetailsTemplate with DataTemplate, and added a Grid (or a StackPanel) inside to format the details. If I add to the markup the TextBlocks with bindings to fields, it displays the details just fine.
But how can I set the content of the Grid/StackPanel in the details template programmatically? The Grid/StackPanel controls are null if I try to reference them by name on startup (e.g., in the page Loaded event). I have tried using the Loaded event on the Grid/StackPanel to add the details. That code runs and appears to add the content to the Grid/StackPanel, but nothing actually appears when I click the row. I'm guessing that the problem is that the template/Grid is already loaded and ignores the changes I'm making.
Here's a sample of the code I'm using in the handler for the Loaded event. Even if I do something as simple as this, the details pane doesn't appear when clicking on the row.
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border Background="LightBlue" >
<StackPanel x:Name="resultsDetailsPanel"
Orientation="Vertical"
Loaded="resultsDetailsPanel_Loaded">
</StackPanel>
</Border>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
private void resultsDetailsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel detailsPanel = sender as StackPanel;
TextBlock fieldNameTextBlock = new TextBlock();
fieldNameTextBlock.Text = "TESTING";
detailsPanel.Children.Add(fieldNameTextBlock);
_resultsGridLoaded = true;
}
I actually tried your code and is working. Two things you should check:
Is your _resultsGridLoaded variable initialized as false?
Did you set RowDetailsVisibilityMode="VisibleWhenSelected" on your DataGrid?
UPDATE: For some reason is not working anymore. But I did found two ways you can fix it:
Remove the resultsGridLoaded logic.
If you need that logic, you can add a handler for the SelectionChanged event on the DataGrid, in there you can set the _resultsGridLoaded variable to false so the new StackPanel gets its content added correctly:
And the code behind:
private void resultsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel pane = (StackPanel)sender;
TextBlock newChild = new TextBlock()
{
Text = "New text"
};
pane.Children.Add(newChild);
_resultsGridLoaded = true;
}
private void grid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_resultsGridLoaded = false;
}
Hope this helps

WPF Popup focus in data grid

I'm creating a custom UserControl to be used inside a DataGrid editing template.
It looks like this:
<UserControl
x:Class="HR.Controls.UserPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock x:Name="PART_TextBox" Text="Hello WOrld" />
<Popup Width="234" Height="175" IsOpen="True" StaysOpen="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=PART_TextBox}"
>
<TextBox
x:Name="searchTextBox"
Text=">Enter Name<"/>
</Popup>
</Grid>
</UserControl>
edit:
I've narrowed down the code a bit.
It seems that if I put a Popup with textbox inside the CellEditingTemplate directly the textbox gets focus no problem. When I move that code into a UserControl I can no longer select the textbox when editing the cell.
Is the UserControl doing something funny with the focus ?
The problem is when i edit the cell in the datagrid I get the user control showing up but I can't click in the TextBox searchTextBox. When I click on it the popup closes and the cell goes back to default.
I have tried copying and pasting all the code inside the user control and pasting it directly into the CellEditingTemplate and that interacts the way it should.
I was just wondering if the UserControl did something weird that prevents a popup from gaining focus because it works as expected when directly placed in the CellEditingTemplate ?
Thanks,
Raul
Not sure if this will help anyone, but this helps if you have custom controls in the datagrid with a popup..... this fixed my problem, and it was one line of xaml. I spend the whole day re-reading this forum and then looking at the source for DataGridCell. Hope this helps.
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Focusable" Value="False"></Setter>
</Style>
I had a similar problem where a Popup embedded in a UserControl as a cell editing template would close when certain areas of it were clicked. The problem turned out to be that the WPF Toolkit (and presumably WPF4) DataGrid is very greedy with left mouse clicks. Even when you handle them and set Handled to true, the grid can interpret them as clicking into a different cell.
This thread has the full details, but the fix is to hook into DataGrid.CellEditEnding event and cancel the end edit:
private static void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.Column.GetType() == typeof(DataGridTemplateColumn))
{
var popup = GetVisualChild<Popup>(e.EditingElement);
if (popup != null && popup.IsOpen)
{
e.Cancel = true;
}
}
}
private static T GetVisualChild<T>(DependencyObject visual)
where T : DependencyObject
{
if (visual == null)
return null;
var count = VisualTreeHelper.GetChildrenCount(visual);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(visual, i);
var childOfTypeT = child as T ?? GetVisualChild<T>(child);
if (childOfTypeT != null)
return childOfTypeT;
}
return null;
}
Full credit for this goes to the Actipro thread.
Set FocusManager.IsFocusScope Attached Property on the Popup to True
I had a kinda simular problem, i created a usercontrol containing a textbox, a button and a calendar. Basicaly i create my own datepicker with custom validation logic.
I put this component in a CellEditingTemplate. When i pressed the button, the popup showed, but clicking the popup anywhere caused the cell te stop editing (because the popup was taking focus from the textbox). I solved it by adding code that sais that if the popup is open, the focus of the textbox may not be lost. This did the trick for me.
Also, the in the on loaded event handler of the usercontrol i give focus to the textbox.
In your case it's propably the Usercontrol itsefl that has focus.
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) {
// Don't allow focus to leave the textbox if the popup is open
if (Popup.IsOpen) e.Handled = true;
}
private void Root_Loaded(object sender, RoutedEventArgs e) {
TextBox.Focus();
}

Dynamically add controls to a silveright StackPanel to Create a menu

hello community any one can please say how to add controls dynamically into a Stack Panel
note: what i need is i have to create a menu which get data from database and creates menu items accordingly can any one say how can i create such menus i am new to silver light
I am using silverlight 3 beta and expression blend3 + sketch flow please help me to know how to design those
Excuse the variable names, but here is a code snippet of dynamically adding items to a stack panel
StackPanel split = new StackPanel();
TextBlock expected = new TextBlock();
expected.Text = "Expected Final Bonus";
TextBlock meh = new TextBlock();
meh.Text = Math.Round(((QuoteData)results.First()).ExpectedBonus * 100, 2) + "%";
split.Children.Add(expected);
split.Children.Add(meh);
TextBlock disc = new TextBlock();
disc.Text = "Discretionary Percentage";
TextBlock number = new TextBlock();
number.Text = Math.Round(((QuoteData)results.First()).Discretionary * 100, 2) + "%";
split.Children.Add(disc);
split.Children.Add(number);
Here you can see that I also created the stack panel dynamically, however, you can also create it using XAML.
Something like this should work:
<StackPanel Grid.Row="3" Grid.Column="1" Name="split" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto">
First, if your StackPanel is already in your XAML, handle the Loaded event:
<StackPanel x:Name="spValue" Loaded="spValue_Loaded">
</StackPanel>
private void spValue_Loaded(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = (sender as StackPanel);
stackPanel.Children.Clear();
stackPanel.Children.Add(XamlReader.Load(XElement.Parse(xaml).ToString()) as FrameworkElement);
}
The controls are created with XAMLReader from your stuff loaded from the DB. You can adapt all that to your particular scenario (menu and menu items...)

Resources