Binding to design data in WPF - c#

I have a WPF window containing a ListBox. The ItemsSource is bound to a property of a view model.
<Window x:Class="SimpleWpfApp.View.MainWindow"
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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
<DockPanel>
<ListBox ItemsSource="{Binding SomeThings}" />
</DockPanel>
</Window>
The property of the view model is an observable collection of a custom interface; ISomeInterface. The interface is very simple and is implemented by SomeClass which additionally overrides ToString.
public class MainWindowViewModel
{
public ObservableCollection<ISomeInterface> SomeThings
{
get
{
var list = new List<ISomeInterface>
{
new SomeClass {Value = "initialised"},
new SomeClass {Value = "in"},
new SomeClass {Value = "code"}
};
return new ObservableCollection<ISomeInterface>(list);
}
}
}
public interface ISomeInterface
{
string Value { get; }
}
public class SomeClass : ISomeInterface
{
public string Value { get; set; }
public override string ToString() => Value;
}
When I view the window in Visual Studio 2015 or Blend all is as expected. ToString is called and the ListBox populated.
Blend screenshot
I have created XAML design data which I want to use when in design mode. I have added the design data in a directory called SampleData. I add a design datacontext statement to the window XAML immediately below the first DataContext.
d:DataContext="{d:DesignData Source=/SampleData/Data.xaml}"
This doesn't work. Visual Studio and Blend report 'File or project item not found' regardless of what I use for source path. I have tried /SampleData/Data.xaml, SampleData/Data.xaml, ../SampleData/Data.xaml, ./../SampleData/Data.xaml
Visual Studio and Blend only find Data.xaml if I move it out of the SampleData directory and into the project root. Then I am able to reference it using source path /Data.xaml or Data.xaml. If I use Data.xaml without prefixing / then Visual Studio and Blend report that the file cannot be found.. but find it anyway.
My first question is .. Can I use sample data in a sub-directory? And if so how?
Having successfully referenced Data.xaml in the project root, my window is not calling the overridden ToString so I'm getting a list of class name displayed. The list has the same number of items as the design data so it appears it is using the design data.
My second question is .. Why is the overridden ToString not being called here when it is if the objects are instantiated from code?
I'm aware I can achieve the desired result by specifying an item template.
<ListBox ItemsSource="{Binding SomeThings}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Full source is available for the example application on github
https://github.com/DangerousDarlow/WpfDesignData
UPDATE
Thanks to jstreet for answering. I changed the file properties for data.xaml in the sub directory and am now able to use this as design data. I thought I'd tried this before but I must be mistaken.
I'm still not seeing ToString being called. I tried changing the view model property to List<object> and also List<ISomeInterface> but both resulted in called to object.ToString; deduced by the display of the class name. I'll probably stop looking at this point as I'm not going to be using ToString anyway, I'll bind to the properties I want to display. It would be good to explain the difference in behaviour though.
I'm using Visual Studio 2015 community edition.

Here's some working sample code. You may want to refer to This article - MSDN.
In particular, note how to set properties for your Data.xaml file (Dictionary1.xaml, in my case) in your VS project:
Also note how to create your root object, SomeThings (SomeClasses in my case):
For collections, the root object can be an ArrayList or a custom type that derives from a collection or generic collection...
XAML:
<Window x:Class="WpfApplication277.MainWindow"
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:WpfApplication277"
d:DataContext="{d:DesignData Source=/SampleData/Dictionary1.xaml}"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView ItemsSource="{Binding}"></ListView>
</Grid>
Dictionary1.xaml:
Right-click SampleData folder in your VS project, and select Add\New Item\WPF\Resource Dictionary, replace its contents with your design data. This should make sure your design data can be located in a sub-folder.
<m:SomeClasses xmlns:m="clr-namespace:WpfApplication277">
<m:SomeClass Value="design data 1">
</m:SomeClass>
<m:SomeClass Value="design data 2">
</m:SomeClass>
<m:SomeClass Value="design data 3">
</m:SomeClass>
SomeClasses: List<SomeClass> did NOT work !
public class SomeClasses : List<Object>
{
public SomeClasses() { }
}
SomeClass:
public class SomeClass : ISomeInterface
{
public string Value { get; set; }
public override string ToString() => string.Format("ToString() : {0}",Value);
}
Note that ToString() is definitely being called:

Related

Refactoring property name in code behind does not propagate to XAML file

I'm trying to refactor the property MyText to a new name HerText in the following solution:
MainWindow.xaml.cs
using System.Windows;
namespace resharper_refactoring_xaml
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyText = "Blabla";
DataContext = this;
}
public string MyText { get; set; }
}
}
MainWindow.Xaml
<Window x:Class="resharper_refactoring_xaml.MainWindow"
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:resharper_refactoring_xaml"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding Path=MyText}"></TextBlock>
</Grid>
</Window>
I right click on the property and select Refactor this > Rename. Then I type in a new name for the property, hit Next.
Unfortunately, only the references of MyText in the code-behind are renamed. References to MyText in the XAML file rename intact.
According to this question Resharper should be able to propagate refactorings to XAML files.
Why is the rename not propagating to the XAML file? Is there some sort of Resharper setting I might have overlooked?
The reason behind this seems to be that ReSharper cannot determine that the property name specified in the XAML markup refers to the property defined in the MainWindow class, if the DataContext property is set in code-behind.
Bindings refer to the DataContext of controls as source by default. If it is not detected, the link between the loose markup and the defining type is lost. I cannot tell if this is a bug in ReSharper or a general limitation.
However, there are two simple solutions to this issue that work for me:
Set a design time data context to the type that defines the property here MainWindow.
<Window ...
d:DataContext="{d:DesignInstance Type={x:Type local:MainWindow}}">
Set the data context via binding in XAML instead of code-behind.
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">

WPF binding, C# Generics and nullable type result in Unhandled Exception in Visual Studio Designer

I am having a problem with the Visual Studio designer for a WPF project and the combination of binding to a type using a generic and specifying a nullable type as the generic type.
I have tried to construct a minimal example of the problem:
XAML:
<Window x:Class="TestWpfApp.MainWindow"
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:TestWpfApp"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:TestViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding TestText.Value}"/>
<!--<TextBlock Text="{Binding TestTextValue}"/>-->
</Grid>
</Window>
Code behind:
using System.Windows;
namespace TestWpfApp
{
public class TestGeneric<T>
{
public TestGeneric(T value)
{
Value = value;
}
public T Value { get; }
}
public class TestViewModel
{
public TestGeneric<double?> TestText { get; } = new TestGeneric<double?>(123.456);
public double? TestTextValue => TestText.Value;
}
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new TestViewModel();
InitializeComponent();
}
}
}
The designer fails with this code with the following error message:
System.Runtime.Remoting.RemotingException
[16040] Designer process terminated unexpectedly!
The commented out line in the XAML code does not give the error in the designer window.
Both versions actually work when running the project. It is only the designer that fails.
Does anyone have any idea about what the problem could be?
The d:DataContext design time expression is a very practical trick, and IDE related, it has no impact on the runtime, only affect the design time. Applicable in Visual Studio 2010 and later.
The default constructor is required for a type to be instantiated in XAML.
The option IsDesignTimeCreatable=True tells the designer that it can create the specified class via default constructor. This way it is possible to provide sample data for the UI.
d:DataContext="{d:DesignInstance local:TestViewModel , IsDesignTimeCreatable=True }"
Yong Lu

Listbox is not Populating using binding

I am trying to convert my existing program in c# wpf, using mvvm pattern.
The first part is select the Folder location of the files to be process and populate the listbox
I found an example here using Mvvm Light: WPF OpenFileDialog with the MVVM pattern?
the example in the link above is selecting a Folder.
this is the structure of my project
this is the code of my FileListView.xaml
<UserControl x:Class="MvvmLight1.Views.FilesListView"
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"
xmlns:local="clr-namespace:MvvmLight1.Views"
mc:Ignorable="d"
d:DesignHeight="300" Width="730.029">
<Grid>
<ListBox ItemsSource="{Binding FileNames}" Margin="5,5,5,5"/>
</Grid>
</UserControl>
this is my list which reside in ViewModel\OpenFileDialogVM.cs
public System.Collections.ObjectModel.ObservableCollection<string> FileNames { get; }
= new System.Collections.ObjectModel.ObservableCollection<string>();
this is my code for populating the list. but it doesn't work
var files = System.IO.Directory.EnumerateFiles(SelectedPath, "*", System.IO.SearchOption.AllDirectories);
FileNames.Clear();
foreach (var file in files)
{
FileNames.Add(file);
Console.WriteLine(file);
}
What is wrong with my code above?
Code Update:
On my folder structure I have ViewModel Folder and inside it I have OpenFileDialogVm.css
but why is it that the IDE only recognize the ViewModelLocator.
I even Build the project.
I even set the DataContext in the CodeBehind of FileListView user control but still it doesn't populate the listbox
public partial class FilesListView : UserControl
{
public FilesListView()
{
DataContext = new OpenFileDialogVM();
InitializeComponent();
}
}
Add it to your UserControl:
<UserControl
.....
xmlns:viemodels="clr-namespace:MvvmLight1.ViewModels"
/>
<UserControl.DataContext>
<viemodels:OpenFileDialogVM/>
</UserControl.DataContext>
....
</UserControl>

WPF Communication between User Controls

I'm trying to find the best way to communicate between two User Controls. I have a main XAML window which contains two User Controls which in turn contain various controls. The Code behind of each User Control simply sets the DataContext to a View Model that is associated to it. The View Model contains objects that are bound to the controls.
What I'd like to do is capture when a list box in User Control 1 changes selection, the new selected item be displayed in an edit box in User Control 2. As I'm using View Models I can't declare Dependency Properties so I was wondering what is the accepted way to perform this?
I've attached some basic code to show how I'm setting the controls.
Main Window XAML
<Window x:Class="CommsTest.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommsTest.View"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 />
<local:UserControl2 />
</Grid>
UserControl1 XAML
<UserControl x:Class="CommsTest.View.UserControl1"
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">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="50,110,0,0" Name="comboBox1" VerticalAlignment="Top" Width="199" ItemsSource="{Binding Combo1}" />
</Grid>
UserControl1ViewModel.cs
class UserControl1ViewModel
{
private ObservableCollection<string> combo1 = new ObservableCollection<string>();
public ObservableCollection<string> Combo1
{
get { return combo1; }
}
}
UserControl2.XAML
<UserControl x:Class="CommsTest.View.UserControl2"
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">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="63,84,0,0" Name="textBox1" VerticalAlignment="Top" Width="170" Text="{Binding Path=Text1}" />
</Grid>
UserControl2ViewModel.cs
class UserControl2ViewModel
{
private string text1;
public string Text1
{
get { return text1; }
set { text1 = value; }
}
}
How do I get UserControl2.Text1 to be the selected value of UserControl2.Combo1?
Thanks
While I understand that you are asking how to communicate between UserControls, I would suggest that the answer is to communicate between the view models. This can be easily achieved using delegate objects. In general, you'd need to have a parent view model that is common to the two child view models.
I recently answered a similar question, so I won't duplicate answers. Instead, I would ask you to take a look at the answer from the Passing parameters between viewmodels post here on StackOverflow which explains the solution with code examples.
UPDATE >>>
When I said that you need a common parent to your child view models, I don't mean anything to do with inheritance. I just mean that the parent holds a variable instance of each of the child view models... the parent instantiates the child view models.
Instead of creating the view model instance in the view code behind, you can do it in the parent view model and connect the view models to the views like this:
In Resources:
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:UsersViewModel}">
<Views:UsersView />
</DataTemplate>
Then you just need to display an instance of the view model and the appropriate view will be displayed:
In ParentView:
<ContentControl Content="{Binding ViewModel}" />
In ParentViewModel:
public BaseViewModel ViewModel { get; set; } // Implement INotifyPropertyChanged
Then when you want to display a new view:
ViewModel = new UsersViewModel();
If your child views do not have a BaseViewModel and/or are not interchangable, then you could just add a property for each of them:
public MainViewmodel MainViewModel { get; set; } // Implement INotifyPropertyChanged
public UsersViewmodel UsersViewModel { get; set; } // properly for these properties
Either way, you'll need access to these view models from the parent view if you are going to be able to 'connect them together' with handlers.
I would suggest you, to have only one ViewModel and bind the DataContext to MainWindow.xaml, instead of doing it to each UserControl.
You should also implement INotifyPropertyChanged in your ViewModel to notify the UI whenever you change the value from the code or ViewModel.
Maybe you should think about your self-imposed restriction of not having dependency properties in user controls. MVVM is nice for the overal architecture, but you can overdo it if you put it into every class and control you plan to do.
If your user controls are just controls for the user, they should behave as such. I have never had to communicate with a TextBoxViewModel or ButtonViewModel, those are controls I simply use. Maybe yours is simple, too and does not need it's own viewmodel. Then you could communicate by using dependency properties as all other controls do.

Restricting Input in WPF

I am creating a custom control that when invoked in the XAML can be set to only allow certain types of inputs:
<lib:CustomControl RestrictTo="UnsignedIntegersOnly" ... ></CustomControl>
Where the UnsignedIntegersOnly is part of an Enum containing the set of allowed restrictions.
If the user inputs something that is not allowed, the control will throw a validation error and not allow him to continue to the next form/page/etc.
My vision for implementing this, was to, in the underlying TextBox that makes up this control, bind its text field to a validation rule which will be passed as an input the RestrictTo value that was specified in the CustomControl XAML declaration. Then in that ValidationRule class, handle the RestrictTo specific validation and return whether the validation was successful or not.
This is where I am not quite sure how to proceed. Is it even possible to pass arguments to the ValidationRule in such a seemingly dynamic manner? I am setting a property, RestrictTo, of my control and then passing that to its validation.
If it is possible, how would it be done? What sort of binding or resource linking should I use?
You might be interested in using a MaskedTextBox control, it will restrict what the user can input in the TextBox.
As there's no official control from Microsoft for WPF I would suggest the following from Xceed :
MaskedTextBox (it's free to use :-)
Here you have the syntax of the mask :
http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.mask.aspx
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:MaskedTextBox Mask="0000"></xctk:MaskedTextBox>
</Grid>
</Window>
If you have Visual Studio 2012 you can easily get this package through NuGet :
(right-click on your project )
Note : on my answer to your previous question, I extensively used validation rules in the link I posted but I'd say that if there are some times where you can avoid it through the means of a well-crafted component/control, then it's wise to do so. As #Barn pointed out on your previous question, a generic validation rule might be a hard thing to do and somewhat questionable as you'll have to handle all the types in it, IMO it's a little counter-intuitive as validators and converters are generally specific against being generalist; you're likely to waste more time on it than it's worth and it will be probably less re-usable than you think it could be. (source : my experience)
Below code should get you started. It is a user control and not a custom control. I recomend you get your code working as a user control first and then convert it to a custom control. Bind Valid property to some property in your viewmodel that controls user workflow.
XAML:
<UserControl x:Class="WpfApplication.ValidatingControl"
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" >
<StackPanel Orientation="Horizontal">
<TextBox Name="_textBox" TextChanged="OnTextChanged" Background="LightGray" Width="200"/>
<TextBlock Name="_messageText" Foreground="Red" />
</StackPanel>
</UserControl>
Code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication
{
public partial class ValidatingControl : UserControl
{
public ValidatingControl()
{
InitializeComponent();
}
public enum Restrictions
{
UnsignedIntegersOnly,
SmallIntegersOnly
}
public static readonly DependencyProperty RestrictToProperty =
DependencyProperty.Register("RestrictTo", typeof(Restrictions), typeof(ValidatingControl), new PropertyMetadata(Restrictions.UnsignedIntegersOnly));
public Restrictions RestrictTo
{
get { return (Restrictions)GetValue(RestrictToProperty); }
set { SetValue(RestrictToProperty, value); }
}
public bool Valid
{
get { return (bool)GetValue(ValidProperty); }
set { SetValue(ValidProperty, value); }
}
public static readonly DependencyProperty ValidProperty =
DependencyProperty.Register("Valid", typeof(bool), typeof(ValidatingControl), new UIPropertyMetadata(false));
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
ValidateText(RestrictTo, _textBox.Text);
}
private void ValidateText(Restrictions restrictTo, string text)
{
// validate text, update _messageText, update Valid
}
}
}

Categories