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();
}
Related
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
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.
I have created a "popup" window which is being displayed using a PopupWindowAction as per the Prism documentation. The view is loading just fine, but the ViewModel is not. All the examples I can find just have a simple ViewModel being created in the code behind for the view. My ViewModel needs to be constructed by unity so that dependencies can be injected, but this is being bypassed because the view is being declared in the xaml:
<prism:InteractionRequestTrigger SourceObject="{Binding CustomViewRequest, Mode=OneWay}">
<prism:PopupWindowAction>
<prism:PopupWindowAction.WindowContent>
<views:CustomView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
I have a partial workaround which is to embed a ContentControl (with a region) within the PopupWindowAction.WindowContent. That works in that when I load the view into the region, the ViewModel is created for me. However, each time the Window appears, it is the same size as the total desktop space across all displays.
I was thinking I could implement some code to set the starting position and dimensions of the popup, but I don't have access to the Window because that is being created for me in the PopupWindowAction. I don't want to restrict the size of the underlying ContentControl or View, otherwise the user won't be able to adjust the window size. Plus, that just feels like a workaround!
So how can I get the PopupWindowAction to load the ViewModel using dependency injection? Or if that is not straightforward, how can can access to the Window dimensions and bind them to the viewmodel associated with the view in the ContentControl?
I ran into the same need and was able to get it working with a custom derived PopupWindowAction, to allow its WindowContent to be composed. According to the Prism doc Unity doesn't support the TryResolve extension method, but if you are more familiar with Unity there may be an alternative way to do the TryResolve part that would work in Unity.
So I defined a ComposablePopupWindowAction that adds a WindowContentType dependency property. Then I overrode the Invoke method, to use the service locator to get an instance of the WindowContentType if it is defined.
// a popupWindowAction that allows the DI to compose its view
public class ComposablePopupWindowAction : PopupWindowAction
{
public static readonly DependencyProperty WindowContentTypeProperty =
DependencyProperty.Register(
"WindowContentType",
typeof(Type),
typeof(ComposablePopupWindowAction),
new PropertyMetadata(null),
v => {
Type type = v as Type;
Type frameworkElementType = typeof(FrameworkElement);
// either this is not specified, or if it is then it needs to be a FrameworkElement
return (v == null) || type.IsSubclassOf(frameworkElementType) || (type == frameworkElementType);
}
);
public Type WindowContentType
{
get { return (Type)GetValue(WindowContentTypeProperty); }
set { SetValue(WindowContentTypeProperty, value); }
}
protected override void Invoke(object parameter)
{
ConfigureWindowContent();
base.Invoke(parameter);
}
protected void ConfigureWindowContent()
{
// configure the windowContent if not specified, but a type was
if ((this.WindowContentType != null) && (this.WindowContent == null))
{
// this doesn't appear to be supported in Unity so might need slightly different logic here?
var view = ServiceLocator.Current.TryResolve(this.WindowContentType);
// if can't get thedesired type then base will use the notification
if ((view != null) && (view.GetType() == this.WindowContentType))
{
this.WindowContent = view as FrameworkElement;
}
}
}
}
Then do the interactionRequestTrigger as normal, and specify the view type for the WindowContentType property:
<prism:InteractionRequestTrigger SourceObject="{Binding ConfigurationPopupRequest, Mode=OneWay}">
<inf:ComposablePopupWindowAction IsModal="True" CenterOverAssociatedObject="True" WindowContentType="{x:Type analysis:ConfigurationPopupView}" />
</prism:InteractionRequestTrigger>
I have mainPage.xaml and userControl.xaml.I called userControl in mainPage. mainPage has a Grid named "grd" and userControl has a button named "btn". Now when I will click on button then click event of userControl will be raised. In this event, I want to hide the Grid(that in mainPage.xaml). How can I access mainPage controls in userControls ?
For windows Phone 8 try this:
(((Application.Current as App).RootVisual as PhoneApplicationFrame).Content as Page)
You can access currently displayed Page using this code :
var mainPage = (PhoneApplicationPage)((App)Application.Current).RootFrame.Content;
Check out my answer, in that OP wanted to set visibility of appbar, my answer will work for Grid also.
how to programatically open and close bottomappbar using a eventhandler that exist in a usercontrol back code?
If you are using MVVM (as you stated in your comment to #har07's answer) you should not hide the grid in the main page using an event handler in your user control page. Instead you should bind a command to the button in the control page. This command should change the view model of the main page and this change should be notified by that view model to the main page.
In the next example I use MVVM Light but other MVVM libraries probably work almost the same.
Add a property to the view model of your main page:
public class MainViewModel : BaseViewModel
{
…your code here
private bool _isValid;
public bool IsValid
{
get
{
return _isValid;
}
set
{
_isValid = value;
RaisePropertyChange("IsValid");
}
}
}
In the main page bind Visibility to IsValid.
<Grid Visibility="{Binding IsValid, Converter={StaticResource converter}}">
…content here
</Grid>
Now, the grid is visible if IsValid is true.
Add a command to the view model of the user control:
public class UserControlViewModel : BaseViewModel
{
…your code here
public RelayCommand InvalidateGridCommand { get; private set; }
public UserControlViewModel()
{
InvalidateGridCommand = new RelayCommand( () => InvalidateGrid() );
}
private void InvalidateGrid()
{
var mainvm = SimpleIoc.Default.GetInstance(MainViewModel);
mainvm.IsValid = false;
}
}
In the user control page bind the Button to the Command:
<Button Command="{Binding InvalidateGridCommand}">
Invalidate
</Button>
Now, clicking the button will set IsValid on the MainViewModel to false, which will, in turn, hide the Grid.
I came here looking for an answer for this question and after some tries I could access the main page of my Windows Phone 8.1 app using:
var mainPage = (MainPage)(Window.Current.Content as Frame).Content.
To access the controls declared in the MainPage, you need also to give them a x:Name and change their x:FieldModifier to "Internal" or "Public".
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>