I'm writing a C# WPF Desktop application.
For one window I get XAML-data from a database, which is a serialized Grid with labels and textboxes.
This deserializing works great. I see the labels and textboxes.
After typing in text in the textboxes I press a button.
I now need to know what I typed in. So I pass my grid (which has a name) and I loop through the children of the grid. I get the textboxes but their text is empty or has the value of the original XAML.
Nothing I've typed in is preserved.
The textboxes have no binding.
<Grid Name="GridQuestions">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Label Content="foo: " Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right"/>
<TextBox Tag="CliFoo" Text="Test" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" Height="23" Margin="0" VerticalAlignment="Top" Width="360" />
<Label Content="Bar: " Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right"/>
<TextBox Tag="CliBar" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Left" Height="23" Margin="0" VerticalAlignment="Top" Width="360" />
<Label Content="BlaBla: " Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right"/>
<TextBox Tag="CliBlaBla" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" Height="23" Margin="0" VerticalAlignment="Top" Width="360" />
</Grid>
In the button click I do:
foreach (var textbox in gridQuestions.Children.OfType<TextBox>().Where(textbox => !string.IsNullOrEmpty(textbox.Tag.ToString())))
{
DoSomething(textbox.Text);
}
For the 3 textboxes textbox.Text is always empty except for the first one which is 'test'.
But I don't get the values I typed in.
What am I missing?
EDIT:
I use the technique explained in this post: http://www.codeproject.com/Tips/82990/XAML-Serialization to (de-)serialize the XAML from the database.
This is the actual code I use:
var grid = (Grid)XamlReader.Parse(this.db.GridXaml);
this.QuestionsStackPanel.Children.Clear();
this.QuestionsStackPanel.Children.Add(grid);
As you can see I add it to a pre-existing stack panel.
First of all, you declare "GridQuestions" in the XAML using Name, and not xName. You cannot use controls declared with Name only in the code behind.
Second, I would not recommend populating like that the TextBoxes. Better use binding, or if you have dynamically added text boxes, think about DataTemplates, and some sort of ListView or GridView.
Read about MVVM pattern here
I found the solution. It is a combination of the comments and some more research.
First I changed from Name to x:Name in the XAML.
Next I added the grid like in my post. The suggestion of Clemens to use gridQuestions = (Grid)XamlReader.Parse(...); didn't work for me.
The last and most important part is like Clemens suggested, I was not operating on the correct instance.
Instead of using the name of the grid I'm now getting the first grid from the stackpanel: var grid = this.QuestionsStackPanel.Children.OfType<Grid>().FirstOrDefault();
Now I can read the values of the textboxes and can I move on.
Thanks again for your suggestions.
Related
I have a TextBox, which I want to be multiline and automatically sized to parent:
<TextBox Grid.Row="0" Grid.Column="0"
AcceptsReturn="True"
HorizontalAlignment="Stretch"
IsEnabled="{Binding CanModify}"
Margin="0"
TextWrapping="Wrap"
Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"
VerticalAlignment="Stretch"
/>
Most suggestions to make the TextBlock automatically switch to new line are about adding TextWrapping="Wrap" to it's properties, to prevent it from stretching it's usually AcceptsReturn="True", VerticalScrollBarVisibility="Auto" or manually set width. As you see, the only thing that I haven't yet tried is width, but in my case TextBox should fit to container, which width is unknown (also depends on it's parent). Having properties set as in listing doesn't help: caret never moves to new line and stretches the TextBox and the whole window as well.
What can I do to prevent this TextBox from stretching and to move text to new line automatically?
EDIT: Container is a TabControl. Containing tab:
<TabItem Header="{some binding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabHeight" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="TabWidth" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0"
AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding CanModify}"
Margin="0"
Style="{some style}"
TextWrapping="Wrap"
Text="{Binding Comment, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"
/>
</Grid>
</TabItem>
This TabControl lies in a simple Grid without any specific styles of constraints. Alas, I can't share the whole code of styles, applied to TabControl, but I'd highly appreciate any ideas to try.
SharedSizeGroups you see are intended to stretch the whole TabControl to the widest tab, preventing it from resizing on tab switches and shared only with another tab (there are two in this TabControl).
This doesn't actually describe what I mean, but I'll try to explain. I've been using C# for an year now and never touched WPF. Only recently I've realized how awesome it is and started using it. I'm now facing a problem.
I want to let the user know the password/username are incorrect, so instead of the old WinForms MessageBox I want to make it more pleasent. I thought about creating a grid that tints the application darker, and then I'll be able to show some text on it. However - how's that possible?... Do you have any nicer ideas to show a message for the application (not a popup)? Thanks.
You can create an UserControl with translucent background (like #33000000), and 3-row grid to show title, message and OK button, like bellow:
<UserControl x:Class="ApplicationNameSpace.MessageControl" ... >
<Grid Background="#33000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Background="#FFFFFF" MinHeight="100" MinWidth="200">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="35"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#EEEEEE">
<Label Content="Unable to Login" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<Grid Grid.Row="1" Margin="10,20">
<TextBlock Text="Wrong username or password. Try again." HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
<Grid Grid.Row="2" Background="#EEEEEE">
<Button Content="OK" Width="80" Height="25" />
</Grid>
</Grid>
</Grid>
</UserControl>
To use, you can add the control at the end of your window, and change the visibility to Visible when needs to show it.
<Window x:Class="ApplicationNameSpace.MainWindow"
xmlns:local="clr-namespace:ApplicationNameSpace" ... >
<Grid>
...
<local:MessageControl Name="messageControl" Visibility="Collapsed" />
</Grid>
</Window>
You can also create a generic control that you pass to a show method the title and message content, like MessageBox show method. You can also add the user control element programmatically in the window in this method.
You can use the validation and the INotifyDataError which is on WPF 4.5 and you can show a nice message next to the textbox check this link for example
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 :)
I am trying to get my data to display properly within a GridLayout, which is to be used as a DataTemplate for an Item within ListBox. Here is the code associated with what I am doing:
<Grid Name="FeedItemTemplate">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ProfileImage}" Grid.RowSpan="2" Height="75" Width="75" VerticalAlignment="Center" Margin="1" />
<TextBlock Text="{Binding UserName}" Grid.Column="1" Foreground="#FFC8AB14" FontSize="28" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding TimeStamp}" Grid.Column="2" TextWrapping="Wrap" FontSize="18" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Message}" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" TextWrapping="Wrap" FontSize="24" />
</Grid>
The issue is that using this layout, when TextWrapping is set to Wrap, the Item is displayed correctly, but when scrolling through the ListBox everything is really jittery, you cannot scroll in small increments, and it just jumps all over the place.
Any reason why it does this? As I said, only when TextWrapping is set to Wrap it does this. When its not used, it scrolls fine, but the text is all along one line and off the screen.
Does it keep jumping if you explicitly set the top-level Grid element's width to a fixed size?
For some reason that I do not fully understand, settings the ListBox's ItemsPanel property to a StackPanel might solve your problem:
<UserControl.Resources>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel/>
</ItemsPanelTemplate>
</UserControl.Resources>
...
<ListBox ... ItemsPanel="{StaticResource MyItemsPanelTemplate}"/>
This is a known issue with listbox scrolling in the current ctp when you have variable height items. The workaround for now is to set a fixed height on your listbox item content. You'll probably also notice the scroll bar doesnt properly go to the bottom all the time. The workaround fixes that too.
Reference.