WPF Validation on a child control - c#

I have a UserControl with a TextBox inside like this:
<UserControl x:Class="xxx.CommonControls.Views.InPlaceEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="parent"
mc:Ignorable="d">
<Border Background="Transparent" MouseLeftButtonDown="UIElement_OnMouseLeftButtonDown">
<TextBox x:Name="ButtonEdit"
Text="{Binding ElementName=parent,
Path=Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox >
</Border>
</UserControl>
Usage:
<controls:InPlaceEdit Grid.Row="0"
Grid.Column="1"
Height="25"
Margin="5,0,0,5"
Value="{Binding SelectedPatient.Name, UpdateSourceTrigger=PropertyChanged}" />
Question is: how to move Validation errors to my TextBox? It's because all ValidationErrors are stored in my InPlaceEdit control, and not populated properly to TextBox.
(It's simplified view, but basically shows my problem. In my application I'm using ButtonEdit from DevExpress instead TextBox)

I have done it by removing Text binding from my child control and setting a binding to be exactly same as a parent:
this.Loaded += (sender, args) =>
{
var binding = BindingOperations.GetBinding(this, ValueProperty);
if (binding != null)
{
BindingOperations.SetBinding(ButtonEdit, DevExpress.Xpf.Editors.TextEditBase.TextProperty, binding);
}
};

Related

Bind to DialogResult failed

I have a WPF Window that is meant to be used as a dialog. I am trying to use MVVM and have a view model as the DataContext of this dialog. So the simple confirmation dialog looks like:
<Window x:Class="Provision.Views.ConfirmationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Provision.Views"
mc:Ignorable="d"
Title="ConfirmationDialog" Height="450" Width="800"
DialogResult="{Binding DialogResult, Mode=TwoWay}">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="12" HorizontalAlignment="Right">
<Button Content ="{Binding Resources.Ok, FallbackValue=Ok}" IsDefault="True" Command="{Binding OkCommand}" Margin="12,0" Padding="12,6"/>
<Button Content ="{Binding Resources.Cancel, FallbackValue=Cancel}" IsCancel="True" Command="{Binding CancelCommand}" Padding="12,6"/>
</StackPanel>
<TextBlock Text="{Binding Resources.Confirmation, FallbackValue='Are you sure?'}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DockPanel>
</Window>
The problem I am having is binding to DialogResult. I get an error message
System.Windows.Markup.XamlParseException: 'A 'Binding' cannot be set on the 'DialogResult' property of type 'ConfirmationDialog'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
The property in the view model that it is binding to looks like:
private bool _dialogResult = false;
public bool DialogResult
{
get => _dialogResult;
set
{
if (_dialogResult != value)
{
_dialogResult = value;
RaisePropertyChanged();
}
}
}
I would eventually like to do something like
var result = new ConfirmationDialog().ShowDialog()
But I guess I don't understand the error message or how to overcome it. Can I make it a DependencyProperty in the view model? If so how?

WPF binding not working on custom UserControl

I have been struggling with this for three days now and I feel I am very close to a solution, but I just can't get there.
I am making a sudoku puzzle and I would like to create a custom control to display one of the nine 3x3 grids, so I dan display nine of them and have a nice 9x9 grid.
I have found at least 30 different pages that should explain how to create this but I could not find the solution on each of them.
I think the problem lays in the PartialSudokuGrid because the Values property doesn't seem to get called. Also, no errors are displayed in the output window. Can anyone tell me what I am doing wrong?
Not meaning to dump code and expect someone to fix it, but I am really stuck on this and I feel as if it is just a little change that will make everything work.
Here is my code:
MainWindow:
<Window x:Class="SudokuWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SudokuWPF"
Title="MainWindow" Height="400" Width="400"
DataContext="{Binding PartialSudokuGrid, Source={StaticResource Locator}}">
<UniformGrid Columns="3" Rows="3">
<local:PartialSudokuGrid Values="{Binding ValuesVM}" />
</UniformGrid>
</Window>
ViewModel:
public class PartialSudokuGridVM : ViewModelBase {
private int[] _values;
public PartialSudokuGridVM() {
this.ValuesVM = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
public int[] ValuesVM {
get {
return this._values;
}
set {
this._values = value;
this.RaisePropertyChanged();
}
}
}
UserControl:
<UserControl x:Class="SudokuWPF.PartialSudokuGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=Values}">
<UniformGrid>
<TextBox Text="{Binding [0]}" />
<TextBox Text="{Binding [1]}" />
<TextBox Text="{Binding [2]}" />
<TextBox Text="{Binding [3]}" />
<TextBox Text="{Binding [4]}" />
<TextBox Text="{Binding [5]}" />
<TextBox Text="{Binding [6]}" />
<TextBox Text="{Binding [7]}" />
<TextBox Text="{Binding [8]}" />
</UniformGrid>
</UserControl>
Code behind:
public partial class PartialSudokuGrid : UserControl {
public PartialSudokuGrid() {
InitializeComponent();
}
public int[] Values {
get {
return (int[])GetValue(ValuesProperty);
}
set {
SetValue(ValuesProperty, value);
}
}
public static DependencyProperty ValuesProperty = DependencyProperty.Register("Values", typeof(int[]), typeof(PartialSudokuGrid));
}
Fix:
Like MDoobie suggested, I removed the Self binding from the PartialGridView and cleared the codebehind file (no use anymore).
old:
<local:PartialSudokuGrid Values="{Binding ValuesVM}" />
new:
<local:PartialSudokuGrid DataContext="{Binding ValuesVM}" />
I think you set the Window's DataContext with this line DataContext="{Binding PartialSudokuGrid, Source={StaticResource Locator}}"
It is set the PartialSudokuGrid not the PartialSudokuGridVM (which has the ValuesVM property). Try to set the PartialSudokuGridVm as DataContext.

MVVM-How to Binding ObservableCollection of Strings into ListBox WPF

Normally I bind ObservableCollection to my own classes. But in this case I need to bind ObservableCollection of Strings in ListBox using MVVM into WPF.
My xml is
<Window x:Class="ListBoxDynamic.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:vm="clr-namespace:ListBoxDynamic.ViewModel">
<Grid Margin="0,0,-8,0">
<ListBox Width="100" Height="90" ItemsSource="{Binding ListOfItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton Command="{Binding SelectItemCommand}" CommandParameter="{Binding ElementName=Item}" >
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<TextBlock x:Name="Item" Text="{Binding What I must to write?}" />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
My MainViewModel
private ObservableCollection<string> _listOfItems = new ObservableCollection<string>();
public ObservableCollection<string> ListOfItems
{
get { return _listOfItems; }
set { _listOfItems = value; RaisePropertyChanged("ListOfItems"); }
}
public ICommand SelectItemCommand { get; private set; }
int counter = 1;
public MainViewModel()
{
ListOfItems.Add("Add new Item");
SelectItemCommand = new RelayCommand<object>((x) => ExecuteSelectItemCommand(x));
}
But I don't know that I must to write in Binding TextBlock into ToggleButton.
Since data which back each ListBoxItem is string, hence DataContext of each ListBoxItem will be string.
Hence doing <TextBlock x:Name="Item" Text="{Binding }" /> will show you the string backing the listboxitem in the textblock
there are few issues I notices
the command is available in the view model instead of the data item, so use relative source to bind to command
"{Binding}" can be used to refer to current Item, so no need to use elementname syntax
then instead of placing a textbox inside toggle button you can make use of content presenter to make it more flexible
so this could go like this
<ListBox Width="100"
Height="90"
ItemsSource="{Binding ListOfItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton Command="{Binding SelectItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"
CommandParameter="{Binding}"
Content="{Binding}">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<ContentPresenter />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Binding.UpdateSourceTrigger giving XamlParseException/TargetInvocationException in WPF

I'm trying to apply the trigger as follows:
using SmartRoutePlanner.Models;
...
Map locationMap = new Map();
locationTextBox.DataContext = locationMap;
Binding locationBinding = new Binding("Location");
locationTextBox.SetBinding(TextBox.TextProperty, locationBinding);
locationBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
And my XAML code is this:
...
xmlns:models="clr-namespace:SmartRoutePlanner.Models"
...
<Grid.Resources>
<models:Map x:Key="mapDataSource"/>
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource mapDataSource}" />
</Grid.DataContext>
<TextBox x:Name="locationTextBox" />
What is causing the exceptions?
In XAML it should look like this:
<Window x:Class="Teste.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Text="{Binding PropertyFromDataContext, UpdateSourceTrigger=Explicit}" />
</Grid>
</Window>
Remember that you bind a property from your DataContext!

SelectionChanged Event for ListBox. Pass parameters between pages

I am building a WP7 app that consist on one listbox in the first page to show the id of several task and a detail page to show the details for each task (selected).
I need to pass the task id from the first page to the second one. I know that it is carried out by SelectionChanged event of the listbox.
This is my XAML code:
<phone:PhoneApplicationPage
x:Class="TaskListAlpha03.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls">
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="TaskListListBoxTemplate">
<StackPanel Orientation="Vertical" Margin="0,0,0,20">
<TextBlock Text="{Binding Crm_object_id}" FontSize="32" FontFamily="Segoe WP Bold" Foreground="Gray"/>
<!--<TextBlock Text="{Binding Comment}" Margin="10,0,0,0"/> -->
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,40">
<TextBlock x:Name="ApplicationTitle" Text="TASK LIST ALPHA" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="tasks" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox
x:Name="allTaskListTasksListBox"
ItemsSource="{Binding AllTaskListTasks}"
ItemTemplate="{StaticResource TaskListListBoxTemplate}"
SelectionChanged="allTaskListTasksListBox_SelectionChanged" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>
And this is the cs code:
private void allTaskListTasksListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxItem lbi = ((sender as ListBox).SelectedItem as ListBoxItem);
NavigationService.Navigate(new Uri("/View/Details.xaml?msg=" + lbi.Content.ToString(), UriKind.RelativeOrAbsolute));
}
In the second page I have this sample code to check if it works:
protected override void OnNavigatedTo
(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string msg = "";
if (NavigationContext.QueryString.TryGetValue("msg", out msg))
PageTitle.Text = msg;
}
When I execute the application I have a "NullReferenceException".
Sorry for my english :S and thanks.
A couple of points here...
1) I would use a Tap event on the item rather than a selection changed event. It helps prevent accidental navigation.
2) If you do use the selectionchanged event, try the following code to get your item. Your current code will attempt to fire if an items is de-selected in the list.
private void allTaskListTasksListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(e.AddedItems.Count > 0) // the items that were added to the "selected" collection
{
var mySelectedItem = e.AddedItems[0] as myItemType;
if(null != mySelectedItem) // prevents errors if casting fails
{
NavigationService.Navigate(
new Uri("/View/Details.xaml?msg=" + mySelectedItem.Crm_object_id,
UriKind.RelativeOrAbsolute)
);
}
}
}
The problem is with ListBoxItem lbi = ((sender as ListBox).SelectedItem as ListBoxItem);. You are casting it to ListBoxItem which in fact it is your custom item you bind it to the ListBox. Do something like:
var item = ((sender as ListBox).SelectedItem as YourItem);

Categories