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}">
Related
So I have created a service that is going to connect to my database and grab a few proxies every here and there so it's going to be doing is contiguously, I am going to have to make it async or with a backgroundworker so it won't deadlock the UI.
However, I've gotten to the part where I've setup my relay command and I want to invoke that function that grabs the proxies.
I have created a service that has the function in it, I didnt add the connecting stuff etc yet so this is mostly hypothetical but the question still stands.
public class ProxyDeliveryService
{
public ProxyDeliveryService()
{
}
public Proxy GrabProxy()
{
//Do work..
//Return the proxy
return null;
}
}
How do I append the data to my collection in my ViewModel with a good MVVM approach? No singletons or anything like that.
This here is throwing an error because it's expecting a delegate with a object parameter. Action<object> and a predicate so just like any other RelayCommand
public class ProxyContainerViewModel : ObservableObject
{
private ProxyDeliveryService pds = new ProxyDeliveryService();
public ObservableCollection<Proxy> Proxies { get; set; } = new ObservableCollection<Proxy>();
public RelayCommand Grabproxies { get; set; } = new RelayCommand(pds.GrabProxy(), true);
public ProxyContainerViewModel()
{
}
}
I think you are overcomplicating this. What's wrong with:
public ICommand Grabproxies { get; set; } = new RelayCommand(CreateProxy, true);
private void CreateProxy(object param)
{
Proxies.Add(pds.GrabProxy());
}
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>
I'm struggling to find a solution to the problem of having to maintain two lists.
I'm using MVVM, but don't want my model to use ObservableCollection. I feel this is best to encapsulate and allows me to use different views/patterns (a console for example). Instead of setting up my structure like this:
public class MainWindow {
// handled in XAML file, no code in the .cs file
}
public abstract class ViewModelBase : INotifyPropertyChanged {
// handles typical functions of a viewmodel base class
}
public class MainWindowViewModel : ViewModelBaseClass {
public ObservableCollection<Account> accounts { get; private set; }
}
public class Administrator {
public List<Account> accounts { get; set; }
public void AddAccount(string username, string password) {
// blah blah
}
}
I would like to avoid having two different collections/lists in the case above. I want only the model to handle the data, and the ViewModel to responsible for the logic of how its rendered.
what you could do is to use a ICollectionView in your Viewmodel to show your Model Data.
public class MainWindowViewModel : ViewModelBaseClass {
public ICollectionView accounts { get; private set; }
private Administrator _admin;
//ctor
public MainWindowViewModel()
{
_admin = new Administrator();
this.accounts = CollectionViewSource.GetDefaultView(this._admin.accounts);
}
//subscribe to your model changes and call Refresh
this.accounts.Refresh();
xaml
<ListBox ItemsSource="{Binding accounts}" />
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.
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.