SubreportProcessing event handler cannot access my viewmodel due to being on a different thread - visual-studio-2010

I have a WPF application that is utilizing the reporting tools included with Visual Studio 2010. I've had some other problems that I've solved by creating a graph of objects that are all marked as serializable, etc., as mentioned on various other web pages.
The ReportViewer control is contained in a WindowsFormsHost. I'm handling the SubreportProcessing event of the ReportViewer.LocalReport object to provide the data for the sub report.
The object graph that I'm reporting on is generated in my viewmodel, and that viewmodel holds a reference to it. The SubreportProcessing handler is in my code behind of my window (may not be the best place - but I simply want to get the ReportViewer working at this point).
Here's the problem: In my event handler, I'm attempting to get a reference to my viewmodel using the following code:
var vm = DataContext as FailedAssemblyReportViewModel;
When the handler is called, this line throws an InvalidOperationException with the message The calling thread cannot access this object because a different thread owns it.
I didn't realize the handler might be called on a different thread. How can I resolve this?
I attempted some searches, but all I've come up with is in regards to updating the UI from another thread using the Dispatcher, but that won't work in this case...

I solved this problem using something I believe is a hack, by adding the following function:
public object GetDataContext() {
return DataContext;
}
And then replacing the line of code from my question with:
object dc = Dispatcher.Invoke(new Func<object>(GetDataContext), null);
var vm = dc as FailedAssemblyReportViewModel;
However, this seems like a hack, and I might be circumventing some sort of safety check the CLR is doing. Please let me know if this is an incorrect way to accomplish this.

That's a nasty problem you have there.
Why don't you use in the view a content presenter which you bind to a windows form host?
And in the view model you would have a property of type of type WindowsFormsHost. Also,in the view model's constructor you could set the windows form's host Child property with the report viewer.
After that is smooth sailing, you could use your report viewer anywhere in your code. Something like this:
View:
<ContentPresenter Content="{Binding Path=FormHost}"/>
ViewModel:
private ReportViewer report = new ReportViewer();
private WindowsFormsHost host = new WindowsFormsHost();
public WindowsFormsHost FormHost
{
get {return this.host;}
set
{
if(this.host!=value)
{
this.host = value;
OnPropertyChanged("FormHost");
}
}
}
public ViewModel() //constructor
{
this.host.Child = this.report;
}
After that happy coding. Hope it helps.

Related

MVVMCross MvxDialogFragment Restore Issue - Does not have MvxFragmentPresentationAttribute

I have upgraded to the latest version of MvvmCross (6.4.1) from 4.2.3. I and using Xamarin Android not Xamarin forms
In the view which initiates the dialog I do the following
Create dialog fragment derived from MvxDialogFragment
Assign a view model to it
Then call ShowView on the fragment
However when I rotate the device it fails in OnCreate with the message
Your fragment is not generic and it does not have MvxFragmentPresentationAttribute attribute set!
This did not happen in 4.2.3. The reason I create dialog this way is that I want it to use different view models depending on where I need this dialog. For example I want to show a different list of data, but in the same format in the dialog.
It seems this will only work if we apply the MvxFragmentPresentationAttribute which needs the type of view model to be defined at design time rather than run time.
Is there anything I can do to achieve this
Any help will be appreciated
If you somehow need to specify the ViewModel type at runtime, you can instead of decorating the class with the MvxFragmentPresentationAttribute let it implement, IMvxOverridePresentationAttribute and return it there with the appropriate ViewModel to be presented in.
Something like:
public class MyDialog : MvxDialogFragment, IMvxOverridePresentationAttribute
{
public MvxBasePresentationAttribute PresentationAttribute(MvxViewModelRequest request)
{
return new MvxFragmentPresentationAttribute
{
ActivityHostViewModelType = myDynamicType
};
}
}
Where you implement some kind of logic to get the myDynamicType somewhere.
However, you should be able to use MvxDialogFragmentPresentationAttribute instead though and the presenter will attempt to use the topmost Android Activity to present it in if you provide a null ref as the ActivityHostViewModelType.

Associating ViewModels with Fragments in MVVMCross

We have a couple of Fragments that we use as common controls:
MyCommonHeaderA
MyCommonHeaderB
In our common View class we call base.OnCreate(bundle) and once that has returned we fish out the fragment instances and set their ViewModels
var commonHeaderAFragment = (MyCommonHeaderA)this.SupportFragmentManager.FindFragmentById(Resource.Id.header1banner);
if (commonHeaderAFragment != null)
{
commonHeaderAFragment.ViewModel = this.ViewModel;
}
var commonHeaderBFragment = (MyCommonHeaderB)this.SupportFragmentManager.FindFragmentById(Resource.Id.header2banner);
if (commonHeaderBFragment != null)
{
commonHeaderBFragment.ViewModel = this.ViewModel;
}
Until recently this has been working with no problem. Recently we have upgraded Xamarin and MVVMCross.
Now whenever we rotate the device OnCreate is called and the execution path ends up in MvxFragmentExtensions.OnCreate where it tries to lookup a type for the Fragment using FindAssociatedViewModelTypeOrNull. There is no associated ViewModel type for the Fragment. We never needed to, should we have associated a type?
I did try MvxViewForAttribute and concrete typed ViewModel property but neither of those worked as they wanted to create new VM instances.
I have a solution which is that in the base OnCreate, if we have a bundle try and find the Fragments and set their ViewModel property before base.onCreate and when there is no bundle we set the ViewModel property after OnCreate. It is clunky but works. I just wanted to check if we should have been setting up our Fragments differently so that we would not have hit this issue
There is an example available that uses the MvxCachingFragmentActivity: https://github.com/MvvmCross/MvvmCross-AndroidSupport/tree/master/Samples
In there you don't need to worry about those kind of problems anymore.

Lazy loading list box, in a panorama page

I wanted to add a lazy loading list box(load content when swipe) in a panorama page in one of my windows phone 7 applications. I could however do it using a pivot page. I referred this link
But this is not working with panorama page. Can anyone please help me?
Okay, you're going to need to do one of two things: use the BCL Async package (basically adds async Tasks and such to WP7) or use a background worker. I highly suggest the BCL Async package, it's easy to get on Nuget.
Now, in your ViewModel (you are using MVVM, yes?) the property that it's bound to, let's call it Items should return an ObservableCollection of the item type you need. Now, here's where the magic happens. In the Getter of that property, return a new collection and use a task to fill it. Something like this:
public ObservableCollection<object> Items
{
get
{
ObservableCollection<object> retCollection = new ObservableCollection<object>();
FillCollection(retCollection);
return retCollection;
}
}
public async void FillCollection(ObservableCollection<object> collectionToFill)
{
Task.Factory.StartNew(() =>
{
foreach(object objectToAdd in collectionImGettingThisDataFrom)
{
// We do this using the Dispatcher to
// be sure to pop back into the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(
() => collectionToFill.Add(objectToAdd));
}
}
}
Because FillCollection is async, the Get method will continue and return the current collection. On another thread, the Task that's created will find the data to add, then push it to the UI thread to add it into the collection. This way, you'll be able to lazy load the data only when you ask for it, without completely blocking your UI thread. If it turns out that it's still making your UI slow, you can add the line:
await TaskEx.Delay(25); // Some time in milliseconds. Too much and it will
// take a long time to load the list,
// too little and it will still bog down your UI.
At the end of the foreach block, but not in the Dispatcher invokation.
Happy coding!
Have you looked at the Telerik Rad Controls yet? They have all types of pull to refresh controls. I used them in a recent app I released called "Rad Libs". You can see the controls here http://www.telerik.com/products/windows-phone.aspx and you can also download an app that demos all of their controls. (Disclaimer: I have no affiliation with telerik. I stand to gain nothing from promoting them here)

Display a custom login control in Windows Phone with Mvvm light

Ok, so what I am looking to do is to display some sort of login control (maybe a UserControl with a TextBox and PasswordBox) when the app is started.
In a non-mvvm situation, a way of doing this would be to use the PopUp primitive control, add the usercontrol as a child element and off you go.
In an MVVM situation, i'm a bit confused about how you would achieve a simmilar result.
I have looked into messaging with the DialogMessage and this is fine for displaying a typical MessageBox, but what about a custom usercontrol?
any help would be fantastic! I can't seem to find any demo code of this anywhere.
In a MVVM situation you can use a delegate to let your View open the dialog when the ViewModel requests it.
You define a delegate at the VM:
public Func<LoginResult> ShowLoginDialogDelegate;
In your View you define the function that will be called:
private LoginResult ShowLoginDialog()
{
LoginResult result;
// show a dialog and get the login data
return result;
}
Then you "connect" the delegate and method in the View:
_viewModel = new MyViewModel();
DataContext = _viewModel;
_viewModel.ShowLoginDialogDelegate += ShowLoginDialog;
And now you can use it in your ViewModel e.g. when a command is executed like that:
LoginResult result = ShowLoginDialogDelegate();
An easier answer is to control it's visibility through a View State which with a little manipulation can be made to work through databinding allowing the view model to display the "Logon Page" state when required.
I just recently wrote about this for the Silverlight/XNA series which you can view here.
It would be much simplier if the SL4 DataEventrigger was available but hay ho.

Observe event created through an instance of a Winforms UserControl

In my winforms app, I have a UserControl that contains a DataGridView. I instantiate and load this UserControl when needed into a panel in my Main Form (frmMain). My problem is figuring out how to resond to or listen for events raised in my UC's DataGridView. For example, I want to handle the CellDoubleClick event of the DataGridView in my Main Form rather than through the UC.
Is this possible? I had thought of updating a property when the cell in the grid is double-clicked, and then let my Main form do whatever when that property changes - therefore I thought of using INotifyPropertyChanged. Im not heavily clued up on how to use it in m scenario however, and would deeply appreciate some help in this regard, or if anyone can suggest an alternate solution.
Much thanx!
Your user control must encapsulate some logic, so if you want to handle event of the DataGridView that is in your control the way you've described, you probably missing something in idea of user controls and encapsulation. Technically here two ways to do this:
Make a public property in your user control of type DataGridView.
Make an event wrapper. You will need to create an event in your user control that is raised when DataGridView CellDoubleClick (or any) is rased and in your calling code you will handle this event wrapper.
The second approach is more logical, cos internal logic of your control is incapsulated and you can provide end-user of you component with more logical and meaningful event then CellDoubleClidk or else.
thank u 4 your reply. Sorry for not responding earlier. I did manage to sort this issue out by creating a public event in my UC:
public event DataGridViewCellEventHandler GridRowDoubleClick {
add { dgvTasks.CellDoubleClick += value; }
remove { dgvTasks.CellDoubleClick -= value; }
}
and in my main form, after I instantiate and load the UC
_ucTask.GridRowDoubleClick += new DataGridViewCellEventHandler(TasksGrid_CellDoubleClick);
with the following attached event:
private void TasksGrid_CellDoubleClick( object sender, DataGridViewCellEventArgs e ) {
// do work here!
}
This does work, although I don't know if any of u experts out there foresee a problem with this approach.

Resources