I have a WPF application built with MVVM and am trying to display a custom class in a combobox. I am still getting the Namespace.Asset despite overriding the ToString Method to something easier on the eyes. What am I doing wrong?
XAML code
<ComboBox ItemsSource="{Binding Drivers}" SelectedItem="{Binding SelectedDriver}" Grid.Row="20" Grid.Column="1" Grid.ColumnSpan="3"/>
ViewModel Code
public List<Driver> Drivers
{
get
{
return this.drivers;
}
set
{
this.drivers = value;
this.RaisePropertyChanged("Drivers");
}
}
public Driver SelectedDriver
{
get
{
return this.selectedDriver;
}
set
{
this.selectedDriver = value;
this.RaisePropertyChanged("SelectedDriver");
}
}
One of the custom classes code with overriden ToString
public class ExperimentalDriver : Driver
{
public override DriverResponse GetDriverResponse(double time)
{
... random unrelated code....
}
public override string ToString()
{
return "Experimental Driver";
}
}
You might need to set the ToString() on the base class
Something like:
public class Driver
{
protected string displayName;
public override string ToString()
{
return displayName;
}
}
Then your class constructors for your sub classes would simply set the displayName
public class ExperimentalDriver : Driver
{
public ExperimentalDriver()
{
displayName = "Experimental Driver";
}
}
Ok I figured it out. I have an abstract class "Driver" in that abstract class I added the following code to require all my derived classes to have a ToString() method
public new abstract string ToString();
When I remove it the problem goes away.
Related
In addition to my original post I guess I need to mention that I am using Prism 6.3. Apparently, the compiler doesn't like stuff added to the metadata class that's not in the original partial. Not sure how to resolve this.
Thanks again ... Ed
Ok, I give, UNCLE!
I am trying to add data annotations to my wpf entity framework app. I've tried 6 ways to Sunday with no luck. I put together what is what I consider the most simple example possible and followed all the instructions ... nothing works.
Here goes.
I have a class that is generated by EF (db first).
namespace junk.DataModels
{
public partial class MyClass
{
public string SomeText { get; set; }
}
}
I have another file with the following partial class:
namespace junk.DataModels
{
[MetadataType(typeof(MyClassMetaData))]
public partial class MyClass
{
}
public partial class MyClassMetaData
{
private string _someText;
[Required(ErrorMessage = "Required")]
public string SomeText
{
get { return _someText; }
set { SetProperty(ref _someText, value); }
}
}
}
In my ViewModel I define:
private MyClass _mc;
public MyClass MC
{
get { return _mc; }
set
{
SetProperty(ref _mc, value);
}
}
And in the constructor:
MC = new MC();
MC.SomeText = "Hello World.";
Lastly, in my xaml:
I have a single bound control:
<TextBox x:Name="txt" Text="{Binding MC.SomeText,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
ValidatesOnNotifyDataErrors=True,
UpdateSourceTrigger=PropertyChanged }"
/>
According to everything I've read, if I run this and clear the textbox, I should get a validation error. I have tried all combinations of "ValidatesOn" it doesn't seem to make a difference. Can someone take pity on me and share the secret sauce? I must be missing something simple. If I bind to the metadataclass it works but that is kinda defeating the purpose.
Any help would be great!
Try adding the following static constructor to your buddy class "MyClass". It will register the metadata against your EF class so the Validator can find the Data Annotations:
static MyClass()
{
// Register the metadata against our EF data object.
// This will ensure the Validator find the annotations
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(MyClass),
typeof(MyClassMetaData)),
typeof(MyClass)
);
}
You could also try running a unit test to confirm whether the Validator has used your annotation, before adding the complexity of the GUI:
[TestMethod]
public void TestAnnotations()
{
MyClass c = new MyClass();
// Manually validate the MyClass object
List<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext context = new ValidationContext(c, serviceProvider: null, items: null);
bool isValid = Validator.TryValidateObject(c, context, validationResults, validateAllProperties: true);
Assert.IsFalse(isValid, "Validation should fail because we didn't set SomeText");
}
I'm studying MEF, and try to use Export attribute to export a property, and import it in an other class.
But my problem is that I want to change this property and the other class can import a new value.
For example,
[Export]
public class A{
[Import("Notes")]
public string Description{get;set;}
}
[Export]
public class B{
[Export("Notes")]
public string Text{get;set;}
}
I want once I change the Text of class B, the A.Description can get changed too.
So, how can I implement this?
Any good idea?
This approach would work for most reference type but not with string which is immutable. This means that after you change the value of B.Text, the objects referenced by A.Description and B.Text will no longer be the same (you can use Object.ReferenceEquals to test this).
One way to do what you are after using MEF is to export/import a method instead of the property:
[Export]
public class A
{
public string Description { get { return GetDescription(); } }
[Import("GetNotes")]
Func<string> GetDescription;
}
[Export]
public class B
{
public string Text { get; set; }
[Export("GetNotes")]
string GetText()
{
return Text;
}
}
Finally note that there are other ways to do this. The most common in .NET is with events.
I'm trying to work out an issue I'm having with implementing MVVM in WPF. My Contact class below is my model that's being populated by Entity Framework.
public class Contact : INotifyPropertyChanged
{
public string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string _lastName;
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
//INotifyPropertyChanged implementation omitted for brevity
}
Here's my ViewModel:
public class ContactViewModel
{
public Contact MyContact { get; set; }
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
So I set my View's datasource to an instance of ContactViewModel, and I'm binding two TextBoxes to MyContact.FirstName and MyContact.LastName. I'm binding a TextBlock to FullName. When I change either of my TextBoxes the Full Name TextBlock doesn't update (obviously, I'm not doing an OnPropertyChanged("FullName") anywhere).
The question is, where do I add OnPropertyChanged("FullName")? I don't necessarily want to modify my model because it's being used elsewhere and I don't to tie it to my ViewModel.
Do I need to rethink my architecture?
Do I need to rethink my architecture?
This can be solved with your current architecture. You just need to propagate the call from your Contact object to your viewModel object.
You will need to implement INotifyPropertyChanged in the viewModel to achieve this.
Something like this:
public class ContactViewModel : INotifyPropertyChanged
{
//INotifyPropertyChanged implementation omitted for brevity...
private Contact _myContact;
public Contact MyContact
{
get
{
return _myContact;
}
set
{
_myContact.PropertyChanged -= myHandler;
_myContact = value;
_myContact.PropertyChanged += myHandler;
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
private void myHandler(Object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("FullName");
}
}
I would also recommend taking a look at MVVM Foundation as this includes a class called PropertyObserver which is designed to make wiring up this sort of thing much easier.
If you want to take the more MVVM pure approach suggested by Big Daddy, you would need to do something like this:
public class ContactViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation omitted for brevity...
// You will require some way of setting this, either via a property
// or the viewModel constructor...
private Contact _myContact;
public string FirstName
{
get { return _myContact.FirstName; }
set
{
_myContact.FirstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
public string LastName
{
get { return _myContact.LastName; }
set
{
_myContact.LastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
Do I need to rethink my architecture?
Maybe...
It looks to me like you're binding your view's properties to your view-model (ContactViewModel) and your model (Contact). Your view can see your public model's properties, etc. via your view-model - I don't think this is good. It looks like a violation of the Law of Demeter. I'd rather see you use your view-model as a wrapper/façade to your model. This creates more work for sure, but I think it gives you a better design and more flexibility. Your view-model will need to implement INotifyPropertyChanged for this to work.
I'm not sure the best way to get this accomplished. Here's my view:
public partial class MyPage : Page
{
[Import]
public MyVM ViewModel
{
get { return DataContext as MyVM ; }
set { DataContext = value; }
}
public String EventName { get; set; }
public MyPage()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{ }
}
And my VM:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ()
{
}
}
This works great. However, I need to get data from either the viewmodel that has my string, or the URL. Either way, I'm not sure the best way to get the string to MyVW using MEF.
I thought ok I'll use Messaging from MVVMLight, but the MyVM class isn't instantiated yet to receive the broadcast from the other ViewModel. So then I thought well, I'll try this:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ([Import("hello")]string hello)
{
}
}
and then put this in the view:
[Export("hello")]
public String MyHello { get; set; }
but that gave me an error. Cannot call SatisfyImports on a object of type 'Form A' because it is marked with one or more ExportAttributes.
So what's the best way to accomplish this?
To share data between views I usually inject a SharedData object into my ViewModels.
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public ISharedData SharedData { get; set; }
I'm also using the Caliburn Micro framework so I'm not passing data around via the URL querystring. By convention CM will parse out URL parameters and inject them into properties on your VM but I'm not sure if this functionality only applies to Windows Phone development.
from here
Examine the Page’s QueryString. Look
for properties on the VM that match
the QueryString parameters and inject
them, performing the necessary type
coercion.
When you say you want to possibly pass data from the view to the vm, that should happen through databinding.
There's a text box and some view model with detail
public class Detail { public string Value {get;set;}}
public class SomeVM
{
public Detail {get;set;}
}
Trying to bind to textbox with this code:
textBox.DataBindings.Add("Text", new SomeVm (), "Detail.Value");
But it says "there's no property to bind". Is there a solution for this problem?
Have you tried:
public string Value
{
get
{
return _someVm.Value;
}
set
{
_someVm.Value=value;
}
}
private readonly SomeVM _someVM=new SomeVM();
...
textBox.DataBindings.Add("Text", _someVm, "Value");