Pass parameters to WPF User Control Library from WPF MVVM app - c#

Greatings, I'm creating a wpf user library control, which has a windows form control. Is possible pass values to properties class library control (not windows forms control properties)?, I have this:
WPF User Control Library (XAML):
<wfi:WindowsFormsHost Height="300" Name="winFormsHost" VerticalAlignment="Top" >
<wfr:ReportViewer x:Name="rptViewer" ProcessingMode="Remote"/>
</wfi:WindowsFormsHost>
....
WPF User Control Library (C#):
public partial class ReportViewer : UserControl
{
public static readonly DependencyProperty UrlReportServerProperty =
DependencyProperty.Register("UrlReportServer", typeof(string), typeof(ReportViewer),
new PropertyMetadata((string)""));
.... // Other Dependecies properties
public string UrlReportServer
{
get { return (string)GetValue(UrlReportServerProperty);}
set { SetValue(UrlReportServerProperty, value); }
}
............ // Other properties
public ReportViewer()
{
InitializeComponent();
this.DataContext = this;
ReportViewerLoad();
}
public void ReportViewerLoad()
{
rptViewer.ProcessingMode = ProcessingMode.Remote;
rptViewer.ServerReport.ReportServerUrl =
new Uri(UrlReportServer);
...... //Pass credentials to server reports and parameters to Report with Properties.
rptViewer.ServerReport.Refresh();
this.rptViewer.RefreshReport();
}
In WPF App, MainPage (XAML) with the reference library:
<WPFControlsSol:ReportViewer HorizontalAlignment="Left" Margin="0,0,0,0"
VerticalAlignment="Top" Width="644"
UrlReportServer="{Binding Url}"
</WPFControlsSol:ReportViewer>
WPF App, MainPage (C#):
public partial class MainPageView : Window
{
public MainPageView()
{
InitializeComponent();
ViewModel viewModel = new ViewModel();
DataContext = viewModel;
}
}
In ViewModel:
public class ViewModel : ViewModelBase
{
private string _url; .... // Other attributes
public string Url
{
get { return _url; }
set
{
if (_url != value)
{
_url = value;
RaisePropertyChanged("Url"); //Notification Method own MVVM Template I use.
}
}
} .... // Other properties
public ViewModel()
{
LoadReport();
}
public void LoadReport()
{
Url = "http://IPSERVER"; .... // Other properties
}
But This not works.

Use the EventHandler Delegate to publish and subscribe an event. WHen information is ready, raise the event and pass along the information required in the EventArgs

You are talking about the nested user controls problem. Catel provides and out of the box solution for you. Take a look at it as an example or just use it as the framework for your app, that is up to you.
Another great feature is that you can map properties between views and view models via easy attributes.

Searching the internet, I found a number of solutions for you. Please take a look at the following pages:
Walkthrough: Using ReportViewer in a WPF Application
Using Report Viewer Control in WPF
Using a Report Viewer Control in Windows Presentation Foundation (WPF)
Using MS ReportViewer in WPF contains a good tip
WindowsFormsHost.PropertyMap Property page on MSDN shows how to translate WPF control properties to WinForms properties and vice versa.
Pass parameters from WPF user control to Windows Form User Control via WindowsFormsHost
Integrate WPF UserControls in WinForms (The other way around, but still provides a valid method for you)
UPDATE >>>
I don't really understand your problem. If you really don't want to follow any advice from those links I gave you, just create a Windows Forms UserControl that hosts the ReportViewer control internally and declare all the properties that you need on that UserControl. Then use XAML like this:
<wfi:WindowsFormsHost Height="300" Name="winFormsHost" VerticalAlignment="Top" >
<YourWinFormsUserControlWithInternalReportViewer UrlServer="Something"
Path="Some/Path/Report.rdl" User="Geert" Password="password" />
</wfi:WindowsFormsHost>

Related

Pass file between pages - UWP C#

I'm wondering what is the best way to pass a file between pages in a UWP app?
I have a UWP app with two pages. In the first page, I have the user open a file with filepicker and load that file into a media player.
I want to pass that same file onto the second page when the user navigates there. I am passing the file over currently as a string which I then am attempting load as a storagefile using GetFileFromPathAsync.
This currently works as I'm able to load the file on the second page but it requires that the user provide broad file system access.
Code on Page 1 (FileLoaded is file path string):
private async void TranscodeMedia_Click(object sender, RoutedEventArgs e)
{
AppWindow appWindow = await AppWindow.TryCreateAsync();
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(TranscodeMedia), FileLoaded);
Code on Page 2:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var fileTransfer = e.Parameter.ToString();
FileName.Text = fileTransfer;
StorageFile PassedFile = await StorageFile.GetFileFromPathAsync(fileTransfer);
I'm wondering if this is the best way to pass the file between pages? I'd rather not require the user to provide broad system access to the app if possible. Any help you can provide is most appreciated!
The best and most standard way in C#/WPF/UWP way is to use a standard pattern that consist of a general ViewModel class (which contains all the common app data that you want to use in the logic layer), put as a field in the static MainPage (or even in the App.xaml.cs class).
I always do it like this:
1) I use the MainPage automatically created as the "shell" of the app, with a property that is the AppViewModel.
The MainPage (and thus the AppViewModel) can be accessed from everywhere in the app, by setting itself as a static field in its own class (the "Current" static field can be called from everywhere in the app... even in a MessageDialog class!).
This is the code for the MainPage (or a shell Page that you wish, but I suggest doing like this, it is a pretty standard way used even by Microsoft), simpler than you think:
public sealed partial class MainPage : Page
{
public AppViewModel ViewModel { get; set; } = new AppViewModel();
public static MainPage Current { get; set; }
public MainPage()
{
this.InitializeComponent();
Current = this;
}
}
THIS is the trick: to make the page static in one field in its
own class, so that that static field will be UNIQUE in the entire app
(this is one of the main features of the "static" word) and, thus, by calling
MainPage.Current.ViewModel you can immediately get any data (in your
specific case, a StorageFile) stored there.
2) The AppViewModel itself is a class that must implement the INotifyPropertyChanged interface, in order to enable bindable properties and functions.
It is common, among Windows developers, to create a base class that implements it and then derive all the classes that needs bindable (i.e. observable) properties from it.
Here it is, exactly how Microsoft itself creates it:
public class BaseBind : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
}
Then you derive AppViewModel class (and all the other model and viewmodel classes) from it… populating it with all the common properties that you need to share across pages.
I have even added a derived property, in order to show how you can share even multiple data types at once, and a function:
public class AppViewModel : BaseBind
{
public AppViewModel()
{
// Usually we initialize all the starting data here, in the viewmodel constructor...
}
// All common app data
private string sampleCommonString;
public String SampleCommonString
{
get { return sampleCommonString; }
set { SetProperty(ref sampleCommonString, value); OnPropertyChanged(nameof(SampleDerivedProperty1)); OnPropertyChanged(nameof(SampleDerivedProperty2)); }
}
public String SampleDerivedProperty1 => "return something based on SampleCommonString";
public String SampleDerivedProperty2
{
get
{
// evaluate in some way SampleCommonString...
return "Same thing as SampleDerivedProperty1, but it allows to add more than just one istruction";
}
}
// This is a property that you can use for functions and internal logic… but it CAN'T be binded to the UI directly
public String SampleNOTBindableProperty { get; set; }
public void SampleFunction()
{
// Insert code, that needs to interact with all the data contained in the viewmodel itself, here...
// The function has to be with NO parameters, in order to work with simple {x:Bind} markup.
// If your function has to access some specific data, you can create a new bindable (or non) property, just as the ones above, and memorize the data there.
}
}
3) Then, in order to access all this from another Page, just create an AppViewModel field in that page, referencing the viewmodel contained in the static mainpage:
public sealed partial class SecondPage : Page
{
public AppViewModel ViewModel => MainPage.Current.ViewModel;
public SecondPage()
{
this.InitializeComponent();
}
}
...and you can easily bind XAML controls properties to the AppViewModel itself:
<TextBlock Text="{x:Bind ViewModel.SampleCommonString, Mode=OneWay}"/>
<TextBox Text="{x:Bind ViewModel.SampleCommonString, Mode=TwoWay}"/>
<Button Content="Sample content" Click="{x:Bind ViewModel.SampleFunction}"/>
(Mode=OneWay is for real-time binding, in order that the property is immediately updated even in the UI, while Mode=TwoWay is used for those properties that can be edited from the control itself, by the user, in order to interact with app logic).
In this mode you will be able to display data and all its changes in real-time!
So... this is the way to keep all the app data at run-time in a
correct and flexible way... by learning it and practicing, in the
future you will use this pattern even in a smarter way, by creating
viewmodels for every object of your application (for example: if
your app need to store your company's customers data, you will have a
"CustomerViewModel" class derived from the BaseBind class, with all
the data of a customer in it) and creating lists like
ObservableCollection<SampleViewModel> to store all of them (ObservableCollection<t> is a collection type that has built-in mechanism to handle list changes, like adding, removing and reordering list items).
Then you will link every observable collection to the ItemsSource property of a control that inherits from ListBase class (tipically: ListView or GridView), creating a DataTemplate to display each list item, like in this example:
<Page
xmlns:vm="using:SampleApp.ViewModelsPath"
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.SampleListOfObjectViewModel, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:SampleObjectViewModel">
<StackPanel>
<TextBlock Text="{x:Bind SampleObjectProperty1, Mode=OneWay}"/>
<TextBlock Text="{x:Bind SampleObjectProperty2, Mode=OneWay}"/>
<Button Click="{x:Bind SampleObjectFunction}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
...and all the data displayed will be updated in real-time whenever you change it!
Hope this all will help you boost your knowledge about how preparing a WPF/UWP logic layer, because all of this works pretty in the same way even for the WPF apps (i.e. the old desktop programs).
Best regards
There are some other ways to implement your requirement about accessing the same file on different pages. But for your scenario, you could use Future-access list in your UWP app.
By picking files and folders, your user grants your app permission to access items that might not be accessible otherwise. If you add these items to your future-access list then you'll retain that permission when your app wants to access those items again later.
Here is the sample code I made
In the first page:
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// add file to the Future Access list
var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
// this token is the key to get the file.
string FALToken = storageItemAccessList.Add(file, "mediaFile");
// in your real scenario, you need to save the token and pass it when you nee
this.Frame.Navigate(typeof(TestPage), FALToken);
}
In the second page:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string token = (string)e.Parameter;
var storageItemAccessList = StorageApplicationPermissions.FutureAccessList;
StorageFile retrievedFile = await storageItemAccessList.GetFileAsync(token);
}
So you don't need the broad file system access if you use Future-access list to keep the permission of files.
For more detailed information, please refer to this document: Track recently used files and folders

Changing the title of the window from a user control

I have a solution using prism with multiple shells. I've almost got it working, but there is one thing that is stumping me. Well, two, but the answer will be the same for both since both are an attribute of the main window.
I need to change the Title of the window when I inject a usercontrol into the Shell.
I'm using ViewModelLocator, IRegionManager, and running all of the navigation through the bootstrapper (Thank you Brian Lagunas for the fantastic pluralsight module, btw)
What I need to do is change the Title of the Shell window when a new view is injected into the content region. The views are all created as UserControls.
I currently have a standard binding for the Title in my shell.xaml code,
Title="{Binding Title}"
and I'm using some very simple code in my ShellViewModel.cs to set it when the Shell initializes.
public string ViewTitle = "<window title here>";
public string Title
{
get { return ViewTitle; }
set { if (ViewTitle != null) SetProperty(ref ViewTitle, value); }
}
This is an old question but I am currently working through the same situation. I am relatively new to MVVM with Prism but wanted to record how I resolved this issue in the event someone else stumbled upon this while searching for an answer.
Create a new class that inherits from BindableBase and add a title property to it:
public class BindableBaseExtended : BindableBase
{
private string _mainTitle;
public string MainTitle
{
get { return _mainTitle; }
set { _mainTitle = value; }
}
}
In your MainWindow (or whatever you are using as a shell), Give your ContentControl a Name
<ContentControl Grid.Row="1" x:Name="mainContent" prism:RegionManager.RegionName="...
In your MainWindow(shell) bind to the content control element by name and path of where we will set the title:
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="22"
Text="{Binding Content.DataContext.MainTitle, ElementName=mainContent}" />
For your main content panels that will be feeding the title change, make them inherit from BindableBaseExtended (previously inheriting BindableBase):
public class ViewBViewModel : BindableBaseExtended
On instantiation of class (someone navigated there) set your MainTitle property:
public ViewBViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
MainTitle = "View B";
eventAggregator.GetEvent<UpdateTitleEvent>().Subscribe(Updated);
}
Your property will now be feed through your user controls to your shell and will change upon navigation. Would love to hear any feedback on how to improve this or point me to where this has already been implemented in a more correct fashion but for now I wanted to share this version in the event any one else is stuck.

Control the view in MVVM

I created a project with the MVVM model, and done so with the view-first approach.
I have a TextBox in my XAML code, along with a Button to pass the data from the TextBox:
<!-- View - XAML code -->
<TextBox
MinWidth="30"
Name="TagId"/>
<Button
Command="{Binding AddTagCommand}"
CommandParameter="{Binding Text, ElementName=TagId}"
Content="Add"/>
When I click the button, I want the TextBox cleared.
According to the Prism manual:
In some cases, the code-behind may contain UI logic code that implements visual behavior that is difficult or inefficient to express in Extensible Application Markup Language (XAML), such as complex animations, or when the code needs to directly manipulate visual elements that are part of the view.
Here's the code behind, and the viewmodel.
//View - code behind
public partial class ApplicationStarterView : UserControl
{
public ApplicationStarterView()
{
}
public ApplicationStarterView(ApplicationStarterViewModel viewModel) : this()
{
DataContext = viewModel;
InitializeComponent();
}
}
//View model
public class ApplicationStarterViewModel : BindableBase
{
private readonly IUnityContainer _container;
public ApplicationStarterViewModel(IUnityContainer container)
{
_container = container;
AddTagCommand = new DelegateCommand<object>(AddTag);
}
public ICommand AddTagCommand { get; private set; }
private void AddTag(object input)
{
//Forward stuff
//Clear TextBox
}
}
Can I in any way squeeze in some code to do a TagId.Clear()?
I'd bind the text to another property on the view model.
That way, you can skip the command parameter and the AddTagCommand can directly read the new Text property, do his adding stuff and then clear it, thus updating TagId.
Completely unrelated piece of advice: it is almost never a good idea to inject the IUnityContainer... if you need to create stuff, use factories.

WPF - MVVM Light, Ribbon Control, nested user controls/views and using events between user controls/views

I am building a WPF application with the aid of MVVM light and Unity.
I have a ribbon control in the main form, with one tab without tab category, and two tabs in a tab category. User control 3 includes user control 2 and user control 1.
I would like to make visible the Tab Category and tabs 2 and 3 when I click in user control 2 and make them invisible when click in user control 3 or 1.
I confused a little bit with the event handling in mvvm structure.
I would like to do this properly with the MVVM rules. Could you give me an example or some really helpful directions how to do it?
Check how it looks like
Your Ribbon or tabs for that matter, should be backed up by a ViewModel, let's say TabCategoryViewModel or Tab2ViewModel and Tab3ViewModel. Inside these ViewModels you inject the IMessanger service (of course register it before if not already done) and create a POCO event message, like SelectedViewMessage
public class SelectedViewMessage
{
public string ViewName { get; set; }
}
Inside your TabCategoryViewModel you would register to listen to this message
public class TabCategoryViewModel : ViewModelBase
{
public readonly IMessanger messageService;
public TabCategoryViewModel(IMessanger messageService)
{
if(messageService == null)
{
throw ArgumentNullException("messageService");
}
this.messageService = messageService;
this.messageService.Register<GoToPageMessage>(this, OnSelectedViewChanged);
}
protected void OnSelectedViewChanged(SelectedViewMessage message)
{
this.IsVisible = message.ViewName == "UserControl2";
}
private bool isVisible;
public bool IsVisible
{
get { return isVisible; }
set
{
if(isVisible != value)
{
isVisible = value;
RaisePropertyChanged();
}
}
}
}
You inject the same IMessanger service into your ViewModel you use to Bind your UserControl2 and fire the message via
var message = new SelectedViewMessage {
ViewName = "UserControl2";
};
this.messangerService.Send<SelectedViewMessage>(message);
This code can be placed inside a ViewSelectedCommand or something similar, and you can use Blend Interactivity Triggers/Actions to bind this to certain events on the View/UserControl
This can be achieved by adding event onPropertyChange whenever user clicks on the tab and add that property in xaml under Visibility tag. Also look at Handling UI Control to understand that mapping in xaml and ti understand event handling from Here
Hope it helps.

How to call method on custom control via ViewModel

I am using the MVVM pattern in my project. It uwas using code-behind.
The issue that I'm having is: Included in my project I have a page transition control that I downloaded from Simple WPF Page transitions.
It worked great in code-behind, the xaml as follows:
<Grid ShowGridLines="False">
<pageTransitions:PageTransition Name="pageTransitionControl" Margin="0" TransitionType="GrowAndFade" />
</Grid>
with this in the window tag:
xmlns:pageTransitions="clr-namespace:WpfPageTransitions;assembly=WpfPageTransitions"
In the code-behind I just ran:
mast.Page mp = new mast.Page();
pageTransitionControl.ShowPage(mp);
When I do the below code-behind, it unloads the current page (mp) and loads the new one (dp)
dist.Page dp = new dist.Page();
pageTransitionControl.ShowPage(dp);
Above "mp" and "dp" are new instances of a UserControl (page). pageTransitionControl is the name of the transition control in the xaml.
Now I would like to get it to run via the ViewModel, without communicating with the view as it is doing above, how can I go about this?
Ideally, the PageTransition control would provide a way for you to set the current page via binding. Assuming that it does not provide a way of doing that, then there are a number of ways of achieving this.
Here are three suggestions, in order of "niceness" (in my opinion).
You can create a new page transition control which either is a wrapper for PageTransition or inherits it. And then add a DependecyProperty for the current page to that class which you can bind to, catch the dependecy property change event and call ShowPage.
Write a class inhering FrameworkElement or DependencyObject, depending on usage, which can bind to a page and to the PageTransition control. This class is then responsible for calling ShowPage on the bound PageTransition control when the current page changes.
Bind the PageTransition control to a property on your model and have code in the model access the control via that property.
Example:
public class MyPageTransition : ContentControl
{
public static readonly DependencyProperty CurrentPageProperty =
DependencyProperty.Register(
"CurrentPage",
typeof(object),
typeof(MyPageTransition),
new PropertyMetadata(DependencyPropertyChanged));
public ContentControl()
{
this.Content = this.pageTransition;
}
public object CurrentPage
{
get { return GetValue(CurrentPageProperty); }
set { SetValue(CurrentPageProperty, value); }
}
protected static void DependencyPropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.Property == CurrentPageProperty)
{
this.pageTransition.ShowPage(CurrentPage);
}
}
private PageTransition pageTransition = new PageTransition();
}

Categories