I am using WPF with MVVM. I need some advise on how I can get the progress message to the UI from the following architecture.
UI - File processing window.
ViewModel - Has properties for Message, ProgressValue
The message is bound to the UI textblock to update the ui on what is happening in the background while the user is working on something else.
When the user click Process file, the ViewModel ProcessFile is invoked.
The viewmodel directly does not process any files. It in turn calls a different assembly which does the processing of the file.
Here are the pieces of code (I could not put the actual code here):
XAML:
<StackPanel>
<TextBlock Text="{Binding Message}" />
<ProgressBar MinWidth="250" Height="25" IsIndeterminate="True" />
</StackPanel>
Currently I have it IsIndeterminate. I will change this to show the percentage complete.
ViewModel
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message=value;
OnPropertyChanged("Message");
}
}
private int _progressValue;
public int ProgressValue
{
get { return _progressValue;}
set
{ _progressValue=value;
OnPropertyChanged("ProgressValue");
}
}
public void StartProcess(string fileName)
{
ThreadStart tStart = delegate()
{
differentAssembly.StartProcess(string fileName);
};
Thread processThread = new Thread(tStart);
processThread.IsBackground = true;
processThread.Start();
}
Now with that said how do I get the progress information from the differentAssembly. This will be a message stating the progress and a percentage.
Thanks for your help.
I had the same question on increasing the progress bar value from different assembly.
Instead of calling thread which of course have good number of advantages I used the Observable pattern. Which uses the delegates and background worker.
If I am right we could use Observable pattern with MVVM.
Let me know if I answered your question.
Could you do something like this?
differentAssembly.StartProcess(fileName, x => ProgressValue = x);
Your StartProcess function is then in charge of updating the progress:
public class DifferentAssembly
{
public void StartProcess(string fileName, Action<int> progressValue)
{
// Initialize progress
progressValue(0);
// Do Some Things
progressValue(25);
// Do More Things
progressValue(50);
// Almost There!
progressValue(75);
// And, I'm Done
progressValue(100);
}
}
Related
I am creating an application that allows users to enter in their details into the database using MVVM and EF. I have a User Control which allows a user to enter a set of details into the database.
Within this application, the view-model contains properties, commands and CRUD operations.
What I want to achieve is to allow the user using this application to enter their details, but once a row has been entered into the database, disable the command altogether or throw an exception stating that one row has been added.
I have a tab control for the user to enter their details and then data grid for them to visually see the details been added.
Is this possible to achieve? How would this be done? Iterate through the rows and then find that row?
Here are my code snippets that my relevant;
View-Model;
private ICommand _AddCommand;
public ICommand AddCommand
{
get
{
if (this._AddCommand == null)
{
this._AddCommand = new RelayCommand(this.SaveExecute, this.SaveCanExecute);
}
return this._AddCommand;
}
}
private bool SaveCanExecute()
{
return !string.IsNullOrEmpty(Name);
}
private void SaveExecute()
{
InsertDetail();
}
xaml;
<Button Content="Save" Grid.Row="9" Name="btnSave" VerticalAlignment="Top" Grid.Column="1" Width="75"
Command="{Binding AddCommand}" />
Any help or guidance is appreciated as I am new to WPF and MVVM.
You could create a a count method using EF, and then within your SaveExecute() command method, call the count, something like so;
public int Count(int _ID)
{
DBEntities context = new DBEntities();
return (from o in context.Entities
where o.EntityID == _ID
select o.EntityID).Count();
}
and then, in your command method;
private void SaveExecute() //RelayCommand
{
if (Count(1) == 0)
{
InsertDetails(this); //Insert method using EF
MessageBox.Show("Items have been addded.");
}
else
{
MessageBox.Show("An Item already exists, unable to add another one.");
}
}
Hope this helps!
The easiest way to do this is to disable the button once the logic enters the SaveExecute() method. This will prevent a new command to be issued.
After that you can enabled it, or leave it disabled.
I'm trying to implement a basic filtered list box in WPF. The user types something and the list is narrowed to the values beginning with the typed phrase.
I have:
a View with:
a TextBox whose Text property is bound to InstitutionFilteringString property in the ViewModel class, which is set as the data context,
a ListBox whose ItemSource property is bound to an ICollectionView named Institutions in the View Model
a ViewModel class with the properties mentioned above.
Code (with irrelevant parts cut out):
class ChooseInstitiutionAndPublisherPageViewModel : WizardPageViewModelBase
{
private ICollectionView _institutions;
public ICollectionView Institutions
{
get
{
return _institutions;
}
set
{
_institutions = value;
NotifyPropertyChanged("Institutions");
}
}
private string _institutionFilteringString;
public string InstitutionFilteringString
{
get
{
return _institutionFilteringString;
}
set
{
_institutionFilteringString = value;
NotifyPropertyChanged("InstitutionFilteringString");
//WORKAROUND
//Institutions.Filter = new Predicate<object>(FilterInstitutions);
Institutions.Refresh();
}
}
public ChooseInstitiutionAndPublisherPageViewModel(WizardViewModel parent)
: base(parent)
{
Institutions = CollectionViewSource.GetDefaultView(CentralRepository.Instance.GetInstitutions());
Institutions.Filter = new Predicate<object>(FilterInstitutions);
}
private bool FilterInstitutions(object obj)
{
//I may refer directly to the field or through the property, it doesn't change anything
if (_institutionFilteringString == null || _institutionFilteringString.Length == 0)
return true;
//some more filtering, irrelevant
//[cut]
}
}
The view and the binding:
<TextBox Text="{Binding Path=InstitutionFilteringString, Mode=TwoWay}" Height="23" Margin="6,6,87,0" Name="institutionNameTextBox" VerticalAlignment="Top" TextChanged="institutionNameTextBox_TextChanged" />
<ListBox Margin="6,35" Name="institutionsListBox" ItemsSource="{Binding Path=Institutions}" IsSynchronizedWithCurrentItem="True" />
So, to the point. The setter for the InstitutionFilteringString is called correctly. Following an advice from here, the setter calls a Refresh() method on the collection view. The FilterInstitutions() method is called.
And now the bug: even though the string was set just before a second, inside the FilterInstitutions method it's null. If I go with the debugger down the call stack, from the point of view of the setter it's still set to the typed value, but inside the filtering method it's null.
In the setter there is a commented-out line of code. Uncommenting it fixes the bug, but it's hardly how it should be done.
What am I doing wrong?
(I'm not sure, but it seems to me as if the setter and the filtering method operated on two different instances of the class. But how is it possible, I create just one instance and the class is not clonable)
EDIT
I'm sorry, it seems I've lied. I've put a breakpoint in the constructor and it seems I indeed create two instances of the class and CollectionViewSource.GetDefaultView returns the same instance of ICollectionView for both. Well, but I want actually to have two views for the same collection. Well, I've followed this answer and it seems to work :)
do you create your Institutions once? and set the
Institutions.Filter = new Predicate<object>(FilterInstitutions)
once? if yes its ok :) can you post your code for this and also the code for FilterInstitutions methode? i do it all the way in my projects and have no problems.
I'm writing an application where I'm attempting to use an MVVM style architecture to handle my data binding (although I'm not using a MVVM specific library, such as MVVM Light). I've got a class which stores all of the information that my application requires, and then each of the screens is assigned a view model to its DataContext, which simply selects the values required for the specific screen, formatting the data if necessary.
As an example, the main data store looks something like this:
class DataStore {
int a, b, c;
string d;
DateTime e;
}
And then the view model allocated to a specific screen, which only uses several of the properties, is something like
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
}
This seems to work fine, when the page loads the data bindings are populated as they should be. However, they do not update automatically when the page loads. I've implemented INotifyPropertyChanged on the DataStore, but it seems that the change event doesn't bubble through to be reflected in the view model. I'm sure I'm going about this a really bad way, so if anyone could help point me in the right direction I'd be very grateful. I've read a stack of guides online, but I seem to be confusing myself more and more!
You have to implement INotifyPropertyChanged and raise PropertyChanged on your VM. In order to do this you will have to listen for DataStore.PropertyChanged. Sample:
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
public MainScreenViewModel()
{
App.DataStore.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "a" || e.PropertyName == "c")
RaisePropertyChanged("data1");
if (e.PropertyName == "e")
RaisePropertyChanged("data2");
};
}
private void RaisePropertyChanged(string propertyName)
{
// raise it
}
}
The only part not covered here is the scenario when e.Day will change in DataStore.
Your approach itself is not the bad and is definitely good enough to start with.
You're binding to the MainScreenViewModel class, so it is that class that needs to implement INotifyPropertyChanged for the UI to get updated when the underlying data gets updated.
You could either move the logic into MainScreenViewModel and raise property change notification there, or handle the PropertyChanged event on DataStore in MainScreenViewModel and raise property changed notification for the appropriate properties.
I am looking at WPF componenents in the toolbox but I cannot find the error provider that is present in 2005/2008.
Is it removed?
the ErrorProvider is a Winforms control. There is no equivalent in WPF. But you will still be able to find in in visual studio 2008 if you create a win forms project.
You might want to take a look at this article on error validation in WPF. It has some useful suggestions and ideas for how to handle validation.
.NET 3.5 added WPF support for IDataErrorInfo: Data validation in .NET 3.5.
First excuse me for commenting a such old discussion, but this could help as I had exactly the same question, and Simon's link helped me to "begin with something"
I could test Simon P.Stevens tutorial, but honestly I didn't like it that much :
Using responseTemplate makes the response slower when displaying the error.
This works only if the rule is always the same for a same class (in my case I have some quantities, that sometimes can be negative, sometimes not).
In the case of an internationalised application(in my case), external libraries have no access to Resources where are translations, so I cannot set appropriated message.
I think using MVVM is very well adapted to manage any situation :
I set my TextBox, with a BorderBrush , and ToolTip, regarding my conditions I will Hide/Display ToolTip and color border :
XAML :
<TextBox x:Name="tbName" Grid.Column="1" Grid.Row="0" Margin="3" LostFocus="tbName_LostFocus" BorderBrush="{Binding BordertbName}"
Text="{Binding MonRepere.Nom}" ToolTipService.ToolTip="{Binding ErrorName}" ToolTipService.IsEnabled="{Binding ToolTipNameEnable}"/>
Code Behind (LostFocus = Leave for whom used to WindowsForm) :
private void tbName_LostFocus(object sender, RoutedEventArgs e)
{
if(tbName.Text=="")
{
this.mv.ErrorName = Properties.Resources.ErrorEmpty;
}
else
{
mv.ErrorName = "";
}
}
Then ViewModel :
private string errorName;
public string ErrorName
{
get { return errorName; }
set
{
errorName = value;
if (value == "")
{
ToolTipNameEnable = false;
BordertbName = Brushes.Gray;
}
else
{
ToolTipNameEnable = true;
BordertbName = Brushes.Red;
}
this.NotifyPropertyChanged("ErrorName");
}
}
private Brush bordertbName;
public Brush BordertbName
{
get { return bordertbName; }
set
{
bordertbName = value;
this.NotifyPropertyChanged("BordertbName");
}
}
private bool toolTipNameEnable;
public bool ToolTipNameEnable
{
get { return toolTipNameEnable; }
set
{
toolTipNameEnable = value;
this.NotifyPropertyChanged("ToolTipNameEnable");
}
}
Just very useful when rules are specific regarding the situation.
I am trying to unit test my WPF databindings using the test suit provided by Microsoft Team System. I would like to be able to test the bindings without showing the window because most of my tests will be for user controls and not actually on a window. Is this possible or is there a better way to do it? The code below works if I show the window, but if I don't, the bindings don't update.
Window1_Accessor target = new Window1_Accessor();
UnitTestingWPF.Window1_Accessor.Person p = new UnitTestingWPF.Window1_Accessor.Person() { FirstName = "Shane" };
Window1 window = (target.Target as Window1);
window.DataContext = p;
//window.Show(); //Only Works when I actually show the window
//Is it possible to manually update the binding here, maybe? Is there a better way?
Assert.AreEqual("Shane", target.textBoxFirstName.Text); //Fails if I don't Show() the window because the bindings aren't updated
While looking for a solution to convert WPF binding errors into exception, I figured out that it can also be used in a unit test project.
The technique is very simple:
Derive a TraceListener that throws instead of logging
Add that listener to PresentationTraceSources.DataBindingSource
Please see the complete solution on GitHub, it includes a unit test project.
Shane, if what you're really worried about is a binding breaking silently, you should look at redirecting the binding traces to somewhere you can examine. I'd start here:
http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx
Other than that, I agree with Gishu that bindings aren't good candidates for unit testing, mainly due to the automagic going on that Gishu mentioned in the "Epilogue". Instead focus on making sure the underlying class behaves correctly.
Note, too, that you can get even more robust traces using the PresentationTraceSources class:
http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx
Hope that helps!
Eyeball it.
This kind of declarative markup rarely breaks.. unless someone goes in manual and screws it up. Even then, you can fix it within minutes. IMHO the cost of writing such tests far outweigh the benefits.
Update[Dec3,08]: Alrighty then.
The test is just testing that the textbox has the value "FirstName" as the Path property of the binding. If I change/refactor FirstName to JustName in the actual data source object, the test would still pass since it is testing against an anonymous type. (Green test when code broken - TDD Antipattern: The Liar)
If your aim is to verify that FirstName has been specified in XAML,
Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);
If you really must catch broken bindings via unit tests (and don't want to show the UI), use the real data source... struggled for a while and came up with this.
[Test]
public void TestTextBoxBinding()
{
MyWindow w = new MyWindow();
TextBox txtBoxToProbe = w.TextBox1;
Object obDataSource = w; // use 'real' data source
BindingExpression bindingExpr = BindingOperations.GetBindingExpression(txtBoxToProbe, TextBox.TextProperty);
Binding newBind = new Binding(bindingExpr.ParentBinding.Path.Path);
newBind.Source = obDataSource;
txtBoxToProbe.SetBinding(TextBox.TextProperty, newBind);
Assert.AreEqual("Go ahead. Change my value.", txtBoxToProbe.Text);
}
Epilogue:
There's some real covert stuff happening in the call to Window.Show(). It somehow magically sets up the DataItem property after which data binding starts working.
// before show
bindingExpr.DataItem => null
bindingExpr.Status => BindingStatus.Unattached
// after show
bindingExpr.DataItem => {Actual Data Source}
bindingExpr.Status => BindingStatus.Active
Once the Binding is Active, I guess you can force textbox updates via code like this..
txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
Once again, I voice my reluctance against this approach. Getting NUnit to run in STA was a pain..
Combining advice I came across in a number of SO posts I wrote the following class which works very well to test WPF bindings.
public static class WpfBindingTester
{
/// <summary>load a view in a hidden window and monitor it for binding errors</summary>
/// <param name="view">a data-bound view to load and monitor for binding errors</param>
public static void AssertBindings(object view)
{
using (InternalTraceListener listener = new InternalTraceListener())
{
ManualResetEventSlim mre = new ManualResetEventSlim(false);
Window window = new Window
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false,
Content = view
};
window.Loaded += (_, __) => mre.Set();
window.Show();
mre.Wait();
window.Close();
Assert.That(listener.ErrorMessages, Is.Empty, listener.ErrorMessages);
}
}
/// <summary>Is the test running in an interactive session. Use with Assume.That(WpfBindingTester.IsAvailable) to make sure tests only run where they're able to</summary>
public static bool IsAvailable { get { return Environment.UserInteractive && Process.GetCurrentProcess().SessionId != 0; } }
private class InternalTraceListener : TraceListener
{
private readonly StringBuilder _errors = new StringBuilder();
private readonly SourceLevels _originalLevel;
public string ErrorMessages { get { return _errors.ToString(); } }
static InternalTraceListener() { PresentationTraceSources.Refresh(); }
public InternalTraceListener()
{
_originalLevel = PresentationTraceSources.DataBindingSource.Switch.Level;
PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
PresentationTraceSources.DataBindingSource.Listeners.Add(this);
}
public override void Write(string message) {}
public override void WriteLine(string message) { _errors.AppendLine(message); }
protected override void Dispose(bool disposing)
{
PresentationTraceSources.DataBindingSource.Listeners.Remove(this);
PresentationTraceSources.DataBindingSource.Switch.Level = _originalLevel;
base.Dispose(disposing);
}
}
}
you can try Guia.
With it you can unit-test your UserControl and check if the data binding is correct. You have to show the window though.
Here is an example. It starts a new instance of your UserControl and sets its DataContext and then checks if the textbox is set to the right value.
[TestMethod]
public void SimpleTest()
{
var viewModel = new SimpleControlViewModel() {TextBoxText = "Some Text"};
customControl = CustomControl.Start<SimpleUserControl>((control) => control.DataContext = viewModel);
Assert.AreEqual("Some Text", customControl.Get<TextBox>("textbox1").Value);
customControl.Stop();
}