I am trying to apply some validation to a rather complex set of technologies for our existing product while implementing a new architecture. Most of this is going well, however I still need to use WinForms for the existing product and therefore need to use the ErrorProvider class for showing validation errors. (The new product will be able to use the new architecture via WPF/MVC too, but I don't have the time or resources for a complete rewrite from scratch to eliminate the WinForms at the moment, hence the mix of technologies)
The basic layout of the new architecture is as follows:
Database -> Model (via NHibernate) -> ViewModel -> Form (using BindingSource)
So my question is, how can I utilise a ValidationResult from a failed DataAnnotation check on a Model's property on a ErrorProvider on the Form?
I have managed to get the list of ValidationResults up to the Form, but setting them to a specific control is alluding me without writing code for each Control, I would prefer to create a generic way of doing this via the BindingSource, possibly in a base Form.
I know I could do this a lot easier if the DataAnnotations were on the ViewModels, but if I did that then I would have to keep all of them up to date if a change was made to the Model/Database table, and that would require plenty of repeated code.
I understand that this question is a bit vague, but considering the fact this spans the majority of the architecture, I can't see a better way of explaining it without writing reams of mostly irrelevant code. If you want extra information, then please ask and I will provide it.
Thanks very much.
not sure if this will help, but see if changing your btn_Save to be like this and then add the GetControlBoundToMember method as well, I am guessing your btnSave method looks similar to the one below. You will also need to add an ErrorProvider Control to your form and call it err1 and move any controls that might be in a groupbox out of the groupbo and place them onto the form, unless you create a recursive method that search through controls that have a collect of controls.
private void btnSave_Click(object sender, EventArgs e)
{
if (_entity != null)
{
try
{
_service.Save(_entity.UpdateEntity());
}
catch (InvalidOperationException ex)
{
//do something here to display errors
listBox1.Items.Clear();
foreach (var r in _entity.Errors)
{
listBox1.Items.Add(r.ErrorMessage);
foreach (var c in GetControlBoundToMember(r.MemberNames.ToList()))
{
err1.SetError(c, r.ErrorMessage);
}
}
}
}
}
private IList<Control> GetControlBoundToMember(IList<string> memberNames)
{
List<Control> controls = new List<Control>();
foreach (Control control in this.Controls)
{
foreach (var mn in memberNames)
{
foreach (Binding binding in control.DataBindings)
{
if (binding.BindingMemberInfo.BindingField == mn) controls.Add(control);
}
}
}
return controls;
}
AB
Related
This is a similar question to this one, but I'm hoping that a better answer has come along in the six years since it was asked.
I have a custom dictionary that I want to use in all textboxes in the application. I don't seem to be able to set the SpellCheck.CustomDictionaries property in a style, like I can with the SpellCheck.IsEnabled property, and I don't want to have to add the setter to every textbox individually.
The answer posted in that question requires hooking in to the Loaded event of every window where you want to use the custom dictionary. We have a large application that is growing all the time, and do not want to have to add handlers for every window, as well as rely on developers remembering to add the code when they add a new window.
Is there any better way to specify a custom dictionary for the whole application? Thanks.
Probably not what you wanted, but I used this EventManager.RegisterClassHandler to handle all of certain type's certain event.
For ex, here I am registering to all TextBoxes' LoadedEvent in MainWindow's constructor:
EventManager.RegisterClassHandler(typeof(TextBox), FrameworkElement.LoadedEvent, new RoutedEventHandler(SetCustomDictionary), true);
and define the RoutedEventHandler:
private void SetCustomDictionary(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
{
textBox.SpellCheck.IsEnabled = true;
Uri uri = new Uri("pack://application:,,,/MyCustom.lex");
if (!textBox.SpellCheck.CustomDictionaries.Contains(uri))
textBox.SpellCheck.CustomDictionaries.Add(uri);
}
}
I have an application that was implemented using the Telerik RadGridView control and Caliburn.Micro MVVM framework. Because of some performance problems, I needed to implement the Telerik VirtualQueryableCollectionView in place of the direct control-to-ObservableCollection binding that was being used. The original code has the ItemsSouce property of the RadGridView was bound to the Prices property of the view model. I had to eliminate that binding and this in the code-behind:
public PricingView(PricingViewModel vm)
{
InitializeComponent();
var dataView = new VirtualQueryableCollectionView()
{ LoadSize=20, VirtualItemCount = vm.Prices.Count };
dataView.ItemsLoading += (sender, e) =>
{
var view = sender as VirtualQueryableCollectionView;
if (dataView != null)
{
view.Load(e.StartIndex, vm.Prices.Skip(e.StartIndex).Take(e.ItemCount));
}
};
this.PricesGridView.ItemsSource = dataView;
}
Since this code only deals with UI specific functionality and it is specific the the view implementation, I am comfortable that this code belongs in the code-behind rather than the ViewModel as it would be a departure from ther MVVM pattern to put a reference to VirtualQueryableCollectionView in the ViewModel. The part that I am not happy with is passing the reference to the ViewModel into the constructor of the View. Is there a good way to get the reference in the code-behind without having to pass the reference in the constructor?
Or is there a better way to do all of this?
My application is implemented with MVVM Light, in my case I used the VirtualQueryableCollectionView class in the ViewModel instead the View.
I did so because I think this class is very similar to the ObservableCollection although it is not part of the core classes.
Actually, VirtualQueryableCollectionView is not limited to the Telerik controls but many other standard controls like the ListView.
The fetch is in my case implemented in the Model.
void MainViewModel()
{
this.Traces = new VirtualQueryableCollectionView<MyEntityClass>()
{
// ViewModel also manages the LoadSize
LoadSize = this.PageSize,
VirtualItemCount = myModel.TotalCount
};
this.Traces.ItemsLoading += (s, args) =>
{
this.Traces.Load(args.StartIndex,
myModel.FetchRange(args.StartIndex, args.ItemCount));
};
}
Not sure what "performance problems" means, but I'm going to assume that means that when you fill the collection from the UI thread it blocks the application long enough it appears unresponsive.
There are two common solutions for this. First is to simply fill your collection from a background thread.
The naive implementation is to simply push the loading onto a ThreadPool thread, then use the Dispatcher to marshall the calls to add items to the ObservableCollection onto the UI thread.
A nicer approach (one that doesn't involve the ViewModel at all) is to use asynchronous bindings. You configure the fallback to some value that indicates to the user you are loading. Sometimes (depending on the situation) you can use a PriorityBinding to gradually fill your UI.
Other alternatives are to load and cache your data beforehand while displaying a splash screen. They're a bit different in WPF, it isn't like the old "display this form for a bit while I do work, then show the main form" mode of winforms. And, of course, there is always the classic data pagination. Its tough to code, but effective. Actually, I should say its tough in the UI. Its easy now in code (database.Skip(pageNumber * pageSize).Take(pageSize)).
Basically, I have a custom control FooControl.
public class FooControl : ItemsControl
{
//Code
}
I need to add some event handling, but rather than using a RoutedEvent I'd much more prefer to use Commanding instead. I'm not really sure how to go about doing this though. If I want it so that when Bar1Property (DependencyProperty) changes it raises the Execute associated execute property. I looked at the ButtonBase code through .NET Reflector and wow, that looks overly complicated. Is adding a command this complex?? Obviously I'd also have to make it so that my control enables/disables certain parts of itself depending on if the CanExecuteChanged is altered or not. But I guess that's another portion.
Here is my OnBar1Changed function so far...
private static void OnBar1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FooControl element = (FooControl)obj;
//What to do here?
}
It sounds like by the way you are asking your question, you want to support commanding in your custom control (like for example Button supports). To do this you I recommend looking at how ICommandSource is implemented. Microsoft gives a great walk through on how you can implement it yourself:
http://msdn.microsoft.com/en-us/library/ms748978.aspx
At the simplest level, all you really need is something like:
FooControl element = obj as FooControl;
if (element == null) return;
if (element.MyCommand != null && element.CanExecute(this.CommandParameter)
{
element.MyCommand.Execute(this.CommandParameter);
}
You'd have to create Dependency Properties for both the Command and the CommandParameter, as well.
Hope that helps,
I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.
I'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();