WPF Display fake Data in designer with Collection of Interface - c#

I want display fake data in Visual Studio Designer. I use View Service Interface way with this aborecence (it is minimal example) :
-ServiceView
-IMainWindow.cs
-ICustomer.cs
-SampleData
-MainWindowDesign.cs
-CustomerDesign.cs
-Data.xaml
IMainWindow.cs
namespace TestDesignSampleData.ServiceView
{
interface IMainWindow
{
ObservableCollection<ICustomer> Customers { get; }
}
}
ICustomer.cs
namespace TestDesignSampleData.ServiceView
{
interface ICustomer
{
string Name { get; set; }
}
}
MainWindowDesign.cs
namespace TestDesignSampleData.SampleData
{
class MainWindowDesign : IMainWindow
{
public ObservableCollection<ICustomer> Customers { get; set; }
public MainWindowDesign()
{
//Customers = new ObservableCollection<ICustomer>();
}
}
}
CustomerDesign.cs
namespace TestDesignSampleData.SampleData
{
class CustomerDesign : ICustomer
{
public string Name { get; set; }
}
}
Data.xaml
<sd:MainWindowDesign xmlns:sd="clr-namespace:TestDesignSampleData.SampleData">
<sd:MainWindowDesign.Customers>
<sd:CustomerDesign Name="Charly Sparow"/>
<sd:CustomerDesign Name="Jacky Chan"/>
<sd:CustomerDesign Name="Dora Exploring"/>
</sd:MainWindowDesign.Customers>
</sd:MainWindowDesign>
This can build and execute, but the data are not displayed and the designer send this error for each line of the collection in Data.xaml :
Error 1 The specified value cannot be assigned to the collection.
The following type was expected: "ICustomer".
For some reason, Customer Design is not recognized as ICustomer.
EDIT:
I think it is Visual Studio designer bug. I dont have this problem with Blend. Maybe the Blend designer's compiler is more advanced.
Minimal Project to reproduce the error :
https://github.com/Orwel/DesignerError/releases/tag/CompiledProject

I think XAML designer has some issue with interpreting arrays/collections with interface type. If interface is not necessity, you can use inheritance instead.
Define your ICustomer -> create base class Customer that implements ICustomer and then create your special classes by inheriting Customer not ICustomer.
As you will have base class you can create array/collection of Customers instead of ICustomer which works correctly with XAML designer in Visual Studio.

I can't quite determine if/when/how you're binding to your MainWindowDesign class, but I've had some luck in using the following structure (customised for your example) to show design time data in WPF.
public class MainWindowDesignFactory
{
public static IMainWindow ViewModel
{
get
{
return new MainWindowDesignFactory().Create();
}
}
public IMainWindow Create()
{
var vm = new MainWindowDesign();
vm.Customers = new ObservableCollection<ICustomer>()
{
new CustomerDesign() { Name = "Charly Sparow" },
new CustomerDesign() { Name = "Jacky Chan" },
new CustomerDesign() { Name = "Dora Exploring" }
};
return vm;
}
private class MainWindowDesign : IMainWindow
{
public ObservableCollection<ICustomer> Customers { get; set; }
}
private class CustomerDesign : ICustomer
{
public string Name { get; set; }
}
}
And then in the XAML file:
<Window
x:Class="DesignTimeDataExample.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:TestDesignSampleData_SampleData="clr-namespace:TestDesignSampleData.SampleData"
Title="MainWindow"
Height="350"
Width="525"
d:DataContext="{x:Static TestDesignSampleData_SampleData:MainWindowDesignFactory.ViewModel}">
<Grid>
<ListBox ItemsSource="{Binding Customers}" />
</Grid>
</Window>
The important elements here are the d:DataContext attribute in the XAML (which does pretty much what you expect it to, set a DataContext while you are in the designer) and the actual MainWindowDesignFactory class, which has a static ViewModel property to bind to.
There are other ways, including using:
d:DataContext="{d:DesignInstance Type={x:Type TestDesignSampleData_SampleData:MainWindowDesign}, IsDesignTimeCreatable=True}"
But I've had more luck with the Factory/Static property combination (the design time creation of an instance using the DesignInstance attribute is a bit wonky, using a manually created Factory gave me more control and less arcane designer errors).
The code above is a little rough but it should give you a starting point.

Related

Conversion of XAML to PNG in MVVM

How to perform XAML conversion (eg whole grid or viewbox) to png file?
I need to do this from the ViewModel level.
Link to the example function that I can not call in ViewModel because I do not have access to the object.
Is there a simple and pleasant way?
The view will be responsible for actually exporting the elements that you see on the screen according to the answer you have linked to.
The view model should initialize the operation though. It can do so in a number of different ways.
One option is to send a loosely coupled event or message to the view using an event aggregator or a messenger. Please refer to the following blog post for more information on subject: http://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/.
Another option is to inject the view model with a loose reference to the view. The view implements an interface and uses either constructor injection or property injection to inject itself into the view model, e.g.:
public interface IExport
{
void Export(string filename);
}
public partial class MainWindow : Window, IExport
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel(this);
}
public void Export(string filename)
{
//export...
}
}
public class ViewModel
{
private readonly IExport _export;
public ViewModel(IExport export)
{
_export = export;
}
public void DoExport()
{
//...
_export.Export("pic.png");
}
}
This way the view model only knows about and have a depdenceny upon an interface. It's has no dependency upon the view and in your unit tests you could easily provide a mock implementation of the IExport interface.
The view model will and should never have any access to the actual elements to be exported though. These belong to the view.
You need something like Interaction - a way for VM to take something from view. If you don't want to install a whole new framework for that, just use Func property:
Your VM:
public Func<string, Bitmap> GetBitmapOfElement {get;set;}
...
//in some command
var bmp = GetBitmapOfElement("elementName");
Then, in your view you have assign something to that property:
ViewModel.GetBitmapOfElement = elementName =>
{
var uiElement = FindElementByName(elementName); // this part you have figure out or just always use the same element
return ExportToPng(FrameworkElement element); // this is the function form the link form your answer modified to return the bitmap instead of saving it to file
}
If you need it async, just change property type to Func<string, Task<Bitmap>> and assign async function in your view
What about dependency properties? Consider the following class that is used to passing data (the data may be a stream or whatever you want):
public class Requester
{
public event Action DataRequested;
public object Data { get; set; }
public void RequestData() => DataRequested?.Invoke();
}
Then you create a usercontrol and register a dependency property of type Requester:
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty RequesterProperty
= DependencyProperty.Register("Requester", typeof(Requester), typeof(MainWindow),
new PropertyMetadata(default(Requester), OnRequesterChanged));
public MyUserControl()
{
InitializeComponent();
}
public Requester Requester
{
get => (Requester) GetValue(RequesterProperty);
set => SetValue(RequesterProperty, value);
}
private static void OnRequesterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> ((Requester) e.NewValue).DataRequested += ((MyUserControl) d).OnDataRequested;
private void OnDataRequested()
{
Requester.Data = "XD";
}
}
And your view model would look something like this:
public class MainWindowViewModel
{
public Requester Requester { get; } = new Requester();
public void RequestData() => Requester.RequestData();
}
In XAML you simply bind the dependency property from your control to the property in your view model:
<Window x:Class="Test.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:Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<local:MyUserControl Requester="{Binding Requester}"/>
</Grid>
</Window>

Design Time Data Interface Implementation

I have a ViewModel class like this
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
if (!IsInDesignMode)
{
throw new InvalidOperationException();
}
}
public MyViewModel(IDataProvider dataProvider)
{
Data = new ObservableCollection<IData>(dataProvider.GetData());
}
public ObservableCollection<IData> Data { get; private set; }
}
Now I want to create some design time data. In my unit tests I am using a mocking framework (Moq) to do this. I don't like the fact that I need to create some Mock implementation of IData in my App project or referencing and using the Mocking framework.
What is the most elegant way to achieve having design time data in such a scenario?
Edit: Not sure if it is relevant but I am using Visual Studio 2012.
Basically you create a stub, not a mock for design time data. The stub can't have any dependencies injected.
public class MyDesignViewModel : ViewModelBase
{
public MyViewModel()
{
Data = new ObservableCollection<IData>(new List<IData>()
{
new MyData() { Value1 = 1, Value2 = "Test" },
...
});
}
public ObservableCollection<IData> Data { get; private set; }
}
Then use it in XAML like this:
<UserControl x:Class="MyView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MyDesignViewModel, IsDesignTimeCreatable=True}">

Lack of knowledge to initializing viewmodel

Okay. So what I need to do is to initialize a ViewModel using a constructor. The problem is I can't create the constructor due lack of knowledge. I'm new to MVVM (or c# in general for that matter) and had to get some help to implement this code:
public class ViewModel
{
private static ViewModel instance = new ViewModel();
public static ViewModel Instance
{
get
{
return instance;
}
}
}
However, I fail to create a constructor to place this code.
DataContext = ViewModel.Instance
It is meant to go into two different pages to pass a value between TextBoxes.
I'm also confused as to whether I should put the ViewModel in both the main window and the page or in just one of the two.
So, anyone can help?
Follow this pattern:
This part is how your model classes should look like,
Even if you use entity framework to create your model they inherit INPC.. so all good.
public class Model_A : INotifyPropertyChanged
{
// list of properties...
public string FirstName {get; set;}
public string LastName {get; set;}
// etc...
}
each view model is a subset of information to be viewed, so you can have many view models for the same model class, notice that in case your make the call to the parameter-less c-tor you get auto instance of a mock model to be used in the view model.
public class ViewModel_A1 : INotifyPropertyChanged
{
public Model_A instance;
public ViewModel()
{
instance = new instance
{ //your mock value for the properties..
FirstName = "Offer",
LastName = "Somthing"
};
}
public ViewModel(Model_A instance)
{
this.instance = instance;
}
}
And this is for your view, if you view in the ide designer you will have a mock view model to show.
public class View_For_ViewModelA1
{
public View_For_ViewModel_A1()
{
//this is the generated constructor already implemented by the ide, just add to it:
DataContext = new ViewModel_A1();
}
public View_For_ViewModel_A1(ViewModel_A1 vm)
{
DataContext = vm;
}
}
XAML Side:
<Window x:Class="WpfApplication1.View_For_ViewModel_A1"
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:ViewModel="clr-namespace:WpfApplication1"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance ViewModel:ViewModel_A1, IsDesignTimeCreatable=True}"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
</Grid>
</Window>
In a more advanced scenario you would want to have a single view model class to relate to several model classes.. but you always should set a view to bind to a single view model.
if you need to kung-fu with your code - make sure you do that in your view model layer.
(i.e. creating a view-model that have several instances of different model types)
Note: This is not the complete pattern of mvvm.. in the complete pattern you can expose command which relate to methods in your model via your view-model and bind-able to your view as well.
Good luck :)
I basically follow this pattern:
public class ViewModelWrappers
{
static MemberViewModel _memberViewModel;
public static MemberViewModel MemberViewModel
{
get
{
if (_memberViewModel == null)
_memberViewModel = new MemberViewModel(Application.Current.Resources["UserName"].ToString());
return _memberViewModel;
}
}
...
}
To bind this to a page is:
DataContext = ViewModelWrappers.MemberViewModel;
And if I'm using more than 1 ViewModel on the page I just bind to the wrapper.
DataContext = ViewModelWrappers;
If you or anybody else, who's new to the MVVM, gets stuck here, for example at the "INotifyPropertyChanged could not be found". I recommend trying some example-MVVM's or tutorials.
Some I found useful:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
https://www.youtube.com/watch?v=EpGvqVtSYjs&index=1&list=PL356CA0B2C8E7548D

ListBox of dead simple MVVM application stays empty - what am I missing?

The XAML of my window looks like this:
<Window x:Class="Binding1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Cronjobs" Height="350" Width="525">
<Grid>
<ListBox HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" ItemsSource="{Binding Cronjobs}" />
</Grid>
</Window>
As visible I bind the ListBox's ItemsSource to the Cronjobs property of the current DataContext. The DataContext is set to an instance of the ViewModel below in the constructor of the code-behind:
public partial class MainWindow : Window
{
private CronjobViewModel cronjobViewModel;
public MainWindow()
{
InitializeComponent();
this.cronjobViewModel = new CronjobViewModel();
this.DataContext = cronjobViewModel;
}
}
The ViewModel looks like this:
class CronjobViewModel : DependencyObject
{
public ObservableCollection<Cronjob> Cronjobs;
public CronjobViewModel( )
{
this.Cronjobs = new ObservableCollection<Cronjob>();
this.Cronjobs.Add( new Cronjob() );
this.Cronjobs.Add( new Cronjob() );
}
}
For quick and simple debugging I manually add some items to the collection for now. That Cronjob class is the actual model which is nothing more than a class with some simple string properties, cut down to the essential part:
class Cronjob {
private string name;
public string Name { get { return this.name; } set { this.name = value; } }
public Cronjob( ) { this.Name = "Herp"; }
}
I am mainly experienced in web development and new to the combination of WPF and MVVM. I spent nearly 10 hours figuring this out now but still do not see the cause. I also tried the DataGrid. I watched the first half of Jason Dolingers Video about MVVM about three times and took a close look on how did it, but it does not work for me, even though I understood the abstract concept of MVVM. I am pretty sure I just unintendedly omitted something in the XAML which should be there, but messing around with display property names and item templates did not help (according to what I found here and there around the internet they are not even necessary). Does anybody see the error in this code?
Sorry for the large code dump, I formatted the "boring" parts in a more compact way.
It's because Cronjobs is a field and you cannot bind to fields. Try changing it into property:
public ObservableCollection<Cronjob> Cronjobs { get; set; }
This should work ;)
public class CronjobViewModel
{
public ObservableCollection<Cronjob> Cronjobs { get; private set; }
public CronjobViewModel()
{
this.Cronjobs = new ObservableCollection<Cronjob>();
this.Cronjobs.Add(new Cronjob());
this.Cronjobs.Add(new Cronjob());
}
}

How do you get a WCF proxy to ctor a child collection?

I am studying today and I did my very first very simple WCF Service. I created a couple of very simple classes as follows (this is simplified a bit) ...
//contact class
public class Contact
{
public int Id { get; set; }
private ObservableCollection<Phone> _contactPhones = new ObservableCollection<Phone>();
public ObservableCollection<Phone> ContactPhones
{
get { return _contactPhones; }
set { _contactPhones = value; }
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
// phone class
public class Phone
{
public string PhoneNumber { get; set; }
public PhoneTypes PhoneType { get; set; }
}
I have a mock repository class that returns a collection of the contact class
class ContactRepositoryMock : IContactRepository
{
private readonly ObservableCollection<Contact> _contactList;
public ContactRepositoryMock()
{
_contactList = new ObservableCollection<Contact>();
Contact contact = this.Create();
contact.Id = 1;
contact.FirstName = "Seth";
contact.LastName = "Spearman";
contact.ContactPhones.Add(new Phone(){PhoneNumber = "864-555-1111",PhoneType = PhoneTypes.Mobile});
contact.ContactPhones.Add(new Phone(){PhoneNumber = "864-555-2222",PhoneType = PhoneTypes.Home});
this.Save(contact);
}
public ObservableCollection<Contact> GetContacts()
{
return _contactList;
}
}
The Save and Create methods are not shown but Save adds to the _contactList collection and Create creates a new instance of contact (notice that the Contact ctor is using eager loading to init the phone _contactPhones collection)
Finally I created a WCF Service wrapper around the ContactRepositoryMock.GetContacts method as follows...
[ServiceContract(Namespace = "")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ContactsService
{
private ContactRepositoryMock _contactRepository = new ContactRepositoryMock();
[OperationContract]
public ObservableCollection<Contact> GetContacts()
{
return _contactRepository.GetContacts();
}
}
The other project is a Silverlight project (which is really what I am s'posed to be studying today.)
In that project I added a Web Reference to my WCF Class and Visual Studio added the proxy class as usual.
I have added a MainPageViewModel in the project as follows:
public class MainPageViewModel : ViewModelBase
{
public MainPageViewModel()
{
if (!IsDesignTime)
{
//GetContacts(); //not shown
}
else
{
var contactList = new ObservableCollection<Contact>();
var contact = new Contact {Id = 1, FirstName = "Seth", LastName = "Spearman"};
contact.ContactPhones.Add(new Phone() { PhoneNumber = "864-555-1111", PhoneType = PhoneTypes.Mobile });
contact.ContactPhones.Add(new Phone() { PhoneNumber = "864-555-2222", PhoneType = PhoneTypes.Home });
contactList.Add(contact);
Contacts= contactList;
}
}
private ObservableCollection<Contact> _contacts;
public ObservableCollection<Contact> Contacts
{
get { return _contacts; }
set
{
if (value!=_contacts)
{
_contacts = value;
OnPropertyChanged("Contacts");
}
}
}
}
AND the following XAML
<UserControl x:Class="MVVMDemo.MainPage"
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"
xmlns:viewModels="clr-namespace:MVVMDemo.ViewModels"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<viewModels:MainPageViewModel x:Key="ViewModels" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
DataContext="{Binding Source={StaticResource ViewModels}}"
Background="White">
</Grid>
</UserControl>
That is a lot of background to get to the error and ultimately what is probably a simple explanation.
The XAML line <viewModels:MainPageViewModel x:Key="ViewModels" /> has a squiggly under it and is returning the error "Cannot create and instance of MainPageViewModel".
I even know the CAUSE of that error. If I disable the contact.ContactPhones.Add ... lines that are in the MainPageViewModel ctor then the error goes away.
Finally, I even know why the error goes away. It is because the Reference file that creates the WCF Proxy class is not initializing the ContactPhones collection.
In other words, in the generated class Reference.cs in the proxy if I change the line that reads...
private System.Collections.ObjectModel.ObservableCollection<MVVMDemo.WSProxy.Phone> ContactPhonesField;
to
private System.Collections.ObjectModel.ObservableCollection<MVVMDemo.WSProxy.Phone> ContactPhonesField = new ObservableCollection<Phone>();
then I can re-enable the contact.ContactPhones.Add... lines and the error goes away. The project compiles and runs.
SO...all that to simply ask...how do I get Visual Studio to generate a proxy class that will init my collection. Or is there a flaw in the way I am doing this? What am I missing?
Sorry for all the detail but I was not sure where in the call chain there might be a failure. I am also going to use all of this detail to ask a few more questions after this one gets answered.
Seth
WCF doesn't call any constructors (at all). Two choices:
add a deserialization callback (MSDN)
handle it in the property
IMO the second is easier:
ObservableCollection<Phone> _contactPhones;
[DataMember]
public ObservableCollection<Phone> ContactPhones
{
get { return _contactPhones ?? (
contactPhones = new ObservableCollection<Phone>());
}
Try adding this line before you add the phone numbers in the constructor for MainPageViewModel:
contact.ContactPhones = new ObservableCollection();
I wouldn't go through a lot of effort to get WCF to generate a child collection. You're work will simply be wiped out if you have to regenerate the proxy service. If you are treating your WCF classes as your Models, I'd create and translate your Models into ViewModels. The benefit here is that your ViewModel implementation can contain the mapping code to explicitly convert the Model to the ViewModel. The ViewModel class won't have to be rewritten or updated every time Reference.cs is generated.

Categories

Resources