Reputation: 19356
I am trying to print a user control with this code:
myUserControlView myView = new myUserControlView();
myUserControlViewModel myViewModel = new myUserControlViewModel(paramItemWithData);
myView.DataContext = myViewModel;
int myFactor;
myFactor = 6;
myView.Measure(new Size(794 * 1, 1122 * 1));
myView.Arrange(new Rect(new Size(794 * 1, 1122 * 1)));
myView.UpdateLayout();
System.Windows.Media.Imaging.RenderTargetBitmap rtb = new System.Windows.Media.Imaging
.RenderTargetBitmap(794 * myFactor, 1122 * myFactor, 96 * myFactor, 96 * myFactor, System.Windows.Media.PixelFormats.Pbgra32);
rtb.Render(myView);
System.Windows.Media.Imaging.PngBitmapEncoder encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
System.IO.MemoryStream ms = new System.IO.MemoryStream();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(rtb));
encoder.Save(ms);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
e.Graphics.DrawImage(img, new System.Drawing.PointF());
The view has only a datagrid which ItemsSource is a propery in the view model. When I print, the result is that I can see the header of the datagrid, but it hasn't rows.
I guess it is something about refresh the view to get the data form the view model, because the view model get the data from the database correctly, but it doesn't display in the print document.
Thanks.
EDIT:
I have notice that the problem is when I get data from database when the method is async. If it isn't asyc, it works fine.
So I will share my code of my mie model:
public MyViewModel()
{
Task.Run(() => getDataAsync().ConfigureAwait(false));
}
private ObservableCollection<MyType> _myItems = new ObservableCollection<MyType>();
public ObservableCollection<MyType> MyItems
{
get { return _myItems; }
set
{
_myItems = value;
base.RaisePropertyChangedEvent("MyItems");
}
}
private async Task getDataAsync()
{
using(ContextEfCore myDbContext = new ContextEfCore(_optionsDbContext))
{
MyItems = new ObservableCollection<MyType>(await myDbCOntext.MyType.ToListAsync());
}
}
private void getData()
{
using(ContextEfCore myDbContext = new ContextEfCore(_optionsDbContext))
{
MyItems = new ObservableCollection<MyType>(await myDbCOntext.MyType.ToList());
}
}
If I use the async method, it doesn't show the rows, but if I use the non async method, it works as expected.
So how could I use the async method to work as expected? I would like to avoid to have the two versions of the method, I would like to have only the async method.
Thanks.
Upvotes: 0
Views: 35
Reputation: 364
A Couple of things that are breaking the MVVM structure (I think you are using the MVVM structure).
Try not call outside of your class in your constructor. As you can see, you cannot await the constructor. This is by design since in a sense, the constructor can be on a different thread. So, in your example, if you call to get data, the context of the thread could be different than the callback. If that happens, your data or the method could cause an error.
You are using a Task structure for a background thread (using Task.Run(), or Task.Start()), instead of a I/O thread. Since fetching data is an I/O bound Task and getDataAsync is IO bound you can await that method.
A good way to do execute this asynchronously is to expose a method that fires after your view is created. MVVM allows the view to know about the view model.
My suggestion would be to expose the getDataAsync Method, or perhaps create a new method like PrepareViewModel, and call that in the view, or if you have one, in the navigation bus.
Here is how code it as an event handler.
The view model:
public class MyViewModel
{
public async Task PrepareViewModelAsync()
{
await getDataAsync();
// add other code to prepare the view model.
// ...
}
private async Task getDataAsync()
{
using(ContextEfCore myDbContext = new ContextEfCore(_optionsDbContext))
{
MyItems = new ObservableCollection<MyType>(await myDbCOntext.MyType.ToListAsync());
}
}
}
The View (Xaml) add a loaded event:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
</Grid>
</Window>
The view code behind, define the event handler:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
// get the view model from the data context
var viewModel = DataContext as MyViewModel;
if (viewModel != null) await viewModel.PrepareViewModelAsync();
}
Upvotes: 1