WPF Textbox Not Reacting to Validation Error - c#

I have a WPF app with a UserControl that contains a Grid which in turn contains several TextBoxes:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0"
Text="Application ID" />
<TextBox Grid.Column="1" Grid.Row="0"
Text="{Binding AzureAppID}"/>
<TextBlock Grid.Column="0" Grid.Row="1"
Text="Vault URL" />
<TextBox Grid.Column="1" Grid.Row="1"
Text="{Binding AzureVaultUrl}" />
</Grid>
I've implemented validation using the INotifyDataErrorInfo interface, which is working and correctly flags errors (e.g., AzureAppID, bound to the first TextBox, is not allowed to be empty).
In fact, if I enter an invalid value in either TextBox, the grid control which contains them lights up with a red border, signifying an error. But neither TextBox gets highlighted to indicate the error.
Why do other controls -- including other TextBoxes embedded within, say, a DataGrid -- react properly, but these ones inside a grid do not?
This is in a project built under Net 4.7.
Chalk This Up To Stupidity
It turns out my validation routine was tagging errors, in the case of the fields in question, with slightly different names from the properties the errors were associated with. Naturally, the WPF engine couldn't match up the error to the textboxes based on what the textboxes were bound to.

I think simple answer is that data grid is designed to listen notification ( say empty not allwoed would be notifified back with appropriate message via eventargs..ofcourse INotifyErrorInfo interface provides that infra behind-the-scene)
When considering plain grid/custom control as you say, you need to take care of that.

Related

Dynamically add UI elements to StackPanel after button pressed

This is my first question here :)
I'm not a professional programmer, I'm only 18 and I haven't studied at university or anything, so please don't hate me if I say something stupid :p
I'm making (or rather trying to make...) an app for Windows 10 UWP and a piece of my xaml code looks like this:
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="12"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="12"/>
</Grid.RowDefinitions>
<Rectangle Margin="0" Grid.Row="1" Fill="White" RadiusX="7" RadiusY="7"/>
<Grid Grid.Row="1" Margin="12,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBox x:Name="textProduct" Margin="0,6,6,6" TextWrapping="Wrap" VerticalAlignment="Top" BorderBrush="#FFCECED2" FontFamily="Segoe UI Light" FontSize="17" PlaceholderText="Produkt..." BorderThickness="1"/>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBox x:Name="textAdditionalInfo" Margin="6,6,0,6" TextWrapping="Wrap" VerticalAlignment="Top" BorderBrush="#FFCECED2" FontFamily="Segoe UI Light" FontSize="17" PlaceholderText="Dodatkowe info..." BorderThickness="1"/>
</StackPanel>
</Grid>
I also have an app bar at the bottom with Add and Delete buttons. I'd like to be able to dynamically add another line of TextBoxes to both StackPanels every time the user presses the Add button and Delete one every time they hit the Delete button. Unfortunetely I have no idea how to achieve this. I've tried to find an answer and I think this can be done by using UserControl, however I have no idea how to implement this.
I hope it's not too comlicated to do, because I don't want to seeem like a person that asks other people to do all my work for me...
If it's a big problem, then it doesn't even need to support deleting the TextBoxes.
I hope you understand what I mean. I'm not native english so sorry for any mistakes ;)
Welcome to XAML, it's well worth the time learning it!
For displaying data XAML has something smart called DataBinding. The general concept is you bind a List (for example all strings you want to display in your StackPanel) to an element in the view. Now whenever you modify that list, the view automatically adapts. StackPanel does not support Binding, but for example ListView does (as seen below)
How about you take a look at this for basic informations about databinding: https://blogs.msdn.microsoft.com/jerrynixon/2012/10/12/xaml-binding-basics-101/
With this in mind, you can do something like this:
<!-- insert at the top -->
<Page.Resources>
<DataTemplate x:Name="MyDataTemplate">
<TextBlock Text="{Binding } />
</DataTemplate>
</Page.Resources>
<!-- insert where you want the list to appear -->
<ListView x:Name="ListView" ItemTemplate="{StaticResource MyDataTemplate}" ItemsSource="{Binding MyCollection}" />
The only hard part for you will be to bind the list to the ListView, but I'm sure you can do it with the tutorial from above ;)
Alernatively, you can name your StackPanel with x:Key="MyStack", and add the items manually:
MyStack.Children.Add(new TextBlock() {Text = "myText"});
However, I can really recommend you to do the DataBinding approach, as it makes interacting with the UI so much easier in bigger projects.

WPF - Commands and referencing objects

So I have got C# 5.0 All-In-One for Dummies, but it doesn't really show a good example for what I want to achieve. Here is my XAML:
<StackPanel Grid.Column="1">
<Grid>
<Image Source="Images/039.JPG" x:Name="example_image"/>
<Image Source="Images/example.png" HorizontalAlignment="Right" VerticalAlignment="Bottom" Panel.ZIndex="999" x:Name="example_logo"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Margin="10" Maximum="100" SmallChange="1" ToolTip="Watermark size in percent (%)" Value="{Binding Path=SliderValue, Source={x:Static Application.Current}}"/>
<TextBlock Grid.Column="1" Text="{Binding Path=SliderValue, Source={x:Static Application.Current}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</StackPanel>
So I have got 2 images, one image 1 (the watermark) on top of the other image 2. And I have got a slider, which I have Binded to a value in my App.xaml.cs.
What I need to do is code a custom command, which will fire when the slider is moved, I need to add some parameters to this command which will be the 2 images names, so I can manipulate these controls using the command.
I can't seem to workout how I would do this, I have made a separate file for my commands called Commands.cs.
Why do I want to send the 2 images names as parameters, well so the command is reusable, if I want to use a different image control.
How would I go about doing this?
There isn't a Command property on the Slider although it's possible to add one (see https://gist.github.com/anonymous/4326429)
A simple option would be to handle the ValueChanged event on the Slider or perhaps if the SliderValue property on App.xaml.cs is a then you could fire your reusable "command" code from its setter.
Side note, you may want to look into the MVVM pattern rather than putting all your code in the code behind.

Controls within grid column don't respect size of column

So, I have a Windows Phone 8.1 application (Windows Store style) and I'm trying to make a simple 3 column layout. I'm running into the problem though that the controls I put in the smaller columns of the grid do not respect the width of the column. The control instead will be some (I assume) default size that is way too big, unless I manually specify in pixels how wide I want the control to be. I don't want to specify pixels though, since I want this to easily work on different screen sizes.
My XAML code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.15*" />
<ColumnDefinition Width="0.70*" />
<ColumnDefinition Width ="0.15*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="A" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Stretch" FontFamily="Global User Interface" FontSize="40" Width="auto"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Stretch" TextWrapping="Wrap" Text="B" VerticalAlignment="Center" Height="576" Width="Auto" TextAlignment="Center" FontSize="206" />
<Button Grid.Column="2" Content="C" HorizontalAlignment="Right" VerticalAlignment="Bottom" FontFamily="Global User Interface" FontSize="40" Height="640" Width="auto"/>
</Grid>
Screenshot showing the overflowing "C" button going beyond the column line of the grid
Funny enough, taking this EXACT same XAML code and putting it in a Windows Phone 8.1 Silverlight application results in it working as I'd expect with the controls being fit to the grid
edit: Actually, even setting widths in pixels has no effect... uhh wat?
Set MinWidth="0" on each of your buttons.

WPF Text of Textbox in Grid during run-time is always empty

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.

WPF clickable area around controls in grid

I have an issue with making a custom title bar for a form. It would contain a search textbox aswell as a few sliders, as seen on this image:
Now, imagine that as the title bar - everything that is not a part of the controls themselves (the textbox which is surrounded by a border element, and the slider) needs to be mousedownable for dragging purposes.
I've tried this:
<Grid MouseDown="TitleGridMouseDown">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="135"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="1" Margin="5 15 5 0" Width="100" MouseDown="TitleGridMouseDown"/>
<Slider Grid.Column="2" Margin="5 15 5 0"/>
<Border Grid.Column="3" CornerRadius="10" BorderThickness="1" BorderBrush="White" Width="180" Height="20" Background="White">
<TextBox Background="Transparent" BorderThickness="0" Height="20"/>
</Border>
</Grid>
However it's of no avail. There's a small part between the two sliders, like a few pixel area, which actually works (DragMove(); in the event itself). I don't have an awful lot of experience this type of things in WPF, but it feels to me like the area shrinks to the control. For instance, nothing changes if I place the border object into a button and try to bind the event to the button.
How should I approach this?
Your problem here is that you want to capture the MouseDown event on the grid element. Since your grid has no Background set, its defaulted to null. The MouseDown event does not get raised. Think of it like it is going through the grid without actually hitting it. A Background=Transparent on your top grid should solve the problem.

Categories