printdialogue from view model in wpf - mvvm

I have a requirement as follows, I want to print the screen elements present on the screen to printer. Implementation is done through MVVM. so If I click on print button on the screen it should display a print dialogue and selecting the printer should proceed with printing all the UI elemnts with their data . I have tried with solution present at print WPF visual from viewmodel but its missing the margings and not displaying properly
Also I have another button Print Preview which should display print preview dialogue to see the preiview.
Thanks in advance.
Regards,
Krishna.

In my opinion the printing of the View in an MVVM application is not the responsiblity or concern of the ViewModel. I believe you are better of doing this from the View.
How I've achieved this before is to use a WPF Behavior on a button - I use a Behavior because I'm using DataTemplates for the View and there isn't a 'code behind' file.
The Behavior exposes a DependencyProperty, this is a binding to what is to be printed or contains what is going to be printed.
XAML:
<Button Margin="0,2,5,2"
HorizontalAlignment="Right"
Content="PRINT"
ToolTip="Prints the current report">
<i:Interaction.Behaviors>
<b:ReportPrintClickBehavior Content="{Binding ElementName=SelectedReportContent, Mode=OneWay}" />
</i:Interaction.Behaviors>
</Button>
To reference the Behavior in the XAML you'll need to reference System.Windows.Interactivity, this can be found on NuGet here.
Code-Behind (Behavior):
In this case I'm printing a FlowDocument hosted inside a FlowDocumentReader.
public sealed class ReportPrintClickBehavior : Behavior<Button>
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content",
typeof(DependencyObject),
typeof(ReportPrintClickBehavior),
new PropertyMetadata(null));
public DependencyObject Content
{
get { return (DependencyObject)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
AssociatedObject.Unloaded += OnUnloaded;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
AssociatedObject.Unloaded -= OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs args)
{
AssociatedObject.Click += OnClick;
}
private void OnUnloaded(object sender, RoutedEventArgs args)
{
AssociatedObject.Click -= OnClick;
}
private void OnClick(object sender, RoutedEventArgs args)
{
var flowDocumentReader = Content.GetVisualDescendent<FlowDocumentReader>();
if (flowDocumentReader != null)
{
flowDocumentReader.Print();
}
}
}

Related

MAUI Blazor App - Run Code On Any Page Load?

Is it possible to run a piece of code on every time a page is loaded whether its being navigated to or any other scenario it may be?
Something like overriding OnNavigate method?
You can override OnAppearing method in the .xaml.cs code of the page, and add a piece of code specified to OnAppearing. OnAppearing means that when the page appears, it will be called.
public partial class Page : ContentPage
{
public Page()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
//a piece of code specified
}
}
I thing that you can use also "Loaded" event:
Add the line Loaded="Page_Loaded" to your ContentPage XAML, like
this:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...
Loaded="Page_Loaded"
>
Add "Page_Loaded" event code to your page code-behind (.cs) file:
private async void Page_Loaded(object sender, EventArgs e)
{
<Your code here>
}
I hope this helps.
An option for when you have both your HTML and your C# code in your .razor file is overwriting the OnInitialized method:
#page "/location"
<h1>Page Title</h1>
#if (items == null)
{
<p>No loadable data</p>
}
#else
{
#foreach (var item in items)
{
<p>#item.Title</p>
}
}
#code {
private List<Class> items;
protected override void OnInitialized()
{
// Load items here
base.OnInitialized();
}
}
This approach is not recommended when you do it for multiple pages, in that case use Jianwei's approach.

mvvmcross custom binding to eventhandler

I am trying to implement LongClick functionality on a view and read the following which provided some info
mvvmcross touch command binding in android
Searched unsuccessfully for IMvxCommand within the code so assume this may be outdated? So I attempted a best effort but cannot get any LongClick functionality - probably due to limited knowledge of C# and eventhandlers. I implemented the following but was not sure of the MvxRelayCommand usage.
public class LongClickEventBinding: MvxBaseAndroidTargetBinding
{
private readonly View _view;
private MvxRelayCommand<JobJob> _command;
public LongClickEventBinding(View view)
{
_view = view;
_view.LongClick += ViewOnLongClick;
}
private void ViewOnLongClick(object sender, View.LongClickEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute();
}
}
public override void SetValue(object value)
{
_command = (MvxRelayCommand<JobJob>)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_view.LongClick -= ViewOnLongClick;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(MvxRelayCommand<JobJob>); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
And
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<View>("LongClick", view => new LongClickEventBinding(view)));
}
And
public ICommand JobSelectedCommand
{
get { return new MvxRelayCommand<JobJob>(NavigateToJobTasks); }
}
public void NavigateToJobTasks(JobJob jobJob)
{
RequestNavigate<JobTaskListViewModel>(new { key = jobJob.JobID });
}
And
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'GroupedList'},'LongClick':{'Path':'JobSelectedCommand'}}"
local:MvxItemTemplate="#layout/listitem_job_old"/>
However when I run code on the emulator and LongClick mouse button on listitem not much happens.
Does the following need to be implemented in the View
public event EventHandler<View.LongClickEventArgs> LongClick;
Any help / pointers appreciated.
For lists, vNext MvxBindableListView has supported ItemLongClick for a while anyway - see
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxBindableListView.cs#L77
Note that this binding hooks into the ListView's ItemLongClick rather than into LongClick
Using this in your axml, you should be able to just do:
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'GroupedList'},'ItemLongClick':{'Path':'JobSelectedCommand'}}"
local:MvxItemTemplate="#layout/listitem_job_old"/>
If this doesn't work then please fire a bug report on Github issues.
If you wanted to do your custom binding on a generic (non list) View, then your code would need to switch to ICommand instead of IMvxCommand, and you also couldn't really pass in the Item argument - so you'd need to just use MvxRelayCommand on the ViewModel.
I've added View-level LongClick support to the issues list - https://github.com/slodge/MvvmCross/issues/165
But for a ListView it is probably the ItemLongClick you are actually interested in

Windows 8 UserControl Frame Object Navigation

Within a XAML user control, the Frame object is null:
this.Frame.Navigate(typeof(FaxPropertiesPage));
How do I navigate between pages with a Windows 8 XAML User Control? I have placed the control within a Callisto Flyout on a XAML page.
The search button below must navigate the user to another XAML page.
I've successfully used the code from app.xaml.cs
Frame frame = Window.Current.Content as Frame;
and then used the standard Navigate code.
There's the nice way and the not-so-nice way:
Both of them start with a navigation service:
public interface INavigationService
{
bool CanGoBack { get; }
void GoBack();
void GoForward();
bool Navigate<T>(object parameter = null);
bool Navigate(Type source, object parameter = null);
void ClearHistory();
event EventHandler<NavigatingCancelEventArgs> Navigating;
}
public class NavigationService : INavigationService
{
private readonly Frame _frame;
public NavigationService(Frame frame)
{
_frame = frame;
frame.Navigating += FrameNavigating;
}
#region INavigationService Members
public void GoBack()
{
_frame.GoBack();
}
public void GoForward()
{
_frame.GoForward();
}
public bool Navigate<T>(object parameter = null)
{
Type type = typeof (T);
return Navigate(type, parameter);
}
So, where do I get the Frame? In App.xaml.cs
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
Window.Current.Activate();
return;
}
// Create a Frame to act as the navigation context and navigate to the first page
var rootFrame = new Frame();
if (DesignMode.DesignModeEnabled)
SimpleIoc.Default.Register<INavigationService, DesignTimeNavigationService>();
else
SimpleIoc.Default.Register<INavigationService>(() => new NavigationService(rootFrame));
I'm using MVVM Light here. This makes life easy because all my viewmodels get created using dependency injection and have their services injected into them.
If you're not using something like MVVM Light and rely on code-behind then you can still make this work: Just make the navigation service static
public class NavigationService : INavigationService
{
public static INavigationService Current{
get;set;}
blah blah blah
}
And change App.xaml.cs to:
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
Window.Current.Activate();
return;
}
// Create a Frame to act as the navigation context and navigate to the first page
var rootFrame = new Frame();
NavigationService.Current= new NavigationService(rootFrame));
}
And you can then access your main Frame anywhere in the app by saying:
NavigationService.Current.Navigate<MyView>();
simple code ( may not be 100% efficient) is :
Frame frame = new Frame();
frame.Navigate(typeof(ExerciseAddPage)

Silverlight 5 + AutoCompleteBox = Bug

Just installed SL5 and the toolkit, that were released few days ago.
The bug happens when you set the Text property of the AutoCompleteBox to string.Empty. It causes the AutoCompleteBox to be in a buggy state. To reproduce the bug:
add an AutoCompleteBox and a Button to the main page. Register to the TextChanged and Click events. This is the code-behind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
auto.Text = string.Empty;
}
private void auto_TextChanged(object sender, RoutedEventArgs e)
{
// Put a break point here.
}
}
In runtime:
1) type "aa" into the autobox.
2) click the button.
3) type "q". ( TextChanged is still invoked).
4) erase the "q" - TextChanged is not invoked.
5) type "q" again - TextChanged is not invoked.
6) and so on, until you pick a new letter. And then it's starts over.
I found a workaround for this strange behavior. You need a control derived from AutoCompleteBox and overrride OnApplyTemplate method to find inner TextBox of AutoCompleteBox.
When inner TextBox TextChanged event fires you need to fire TextChanged event of AutoCompleteBox control manually.
public class CustomAutoComplete : AutoCompleteBox
{
TextBox mytext;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
mytext = GetTemplateChild("Text") as TextBox;
mytext.TextChanged += new System.Windows.Controls.TextChangedEventHandler(mytext_TextChanged);
}
void mytext_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
this.Text = mytext.Text;
OnTextChanged(new RoutedEventArgs());
}
}

MVVM using Page Navigation On Windows Phone 7

The Navigation framework in Windows Phone 7 is a cut down version of what is in Silverlight. You can only navigate to a Uri and not pass in a view. Since the NavigationService is tied to the View, how do people get this to fit into MVVM. For example:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container, IView view)
{
this.container = container;
this.view = view;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView { get { return this.view; } }
public void GoToNextPage()
{
// What do I put here.
}
}
public class View : PhoneApplicationPage, IView
{
...
public void SetModel(IViewModel model) { ... }
}
I am using the Unity IOC container. I have to resolve my view model first and then use the View property to get hold of the view and then show it. However using the NavigationService, I have to pass in a view Uri. There is no way for me to create the view model first. Is there a way to get around this.
Instead of passing the view through the constructor. You could construct the view first via the NavigationService and pass it into the view-model. Like so:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container)
{
this.container = container;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView
{
get { return this.view; }
set { this.view = value; this.view.SetModel(this); }
}
public void GoToNextPage()
{
// What do I put here.
}
}
PhoneApplicationFrame frame = Application.Current.RootVisual;
bool success = frame.Navigate(new Uri("View Uri"));
if (success)
{
// I'm not sure if the frame's Content property will give you the current view.
IView view = (IView)frame.Content;
IViewModel viewModel = this.unityContainer.Resolve<IViewModel>();
viewModel.View = view;
}
If you are using Mvvm Light you could try:
Windows Phone 7 — Navigation between pages using MVVM Light Messaging
(See similar post: Silverlight Navigation using Mvvm-light(oobe)+MEF?)
My opinion is that the view-model should be created and registered at application startup. By placing it inside the root DataContext all pages will automatically get a reference to it without any code-behind or IoC tricks.
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
RootFrame.DataContext = m_ViewModel;
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
m_ViewModel.Activated(PhoneApplicationService.Current.State);
RootFrame.DataContext = m_ViewModel;
}
If you are using MVVM architecture,then you can pass navigationPage after registering using Messenger. Create a model class (say NavigateToPageMessage) with a string(say PageName) variable. You want to pass string from homepage.xaml to newpage.xaml,then in Homepage viewmodel just send the message like this under the command you binded (say HomeNavigationCommand)
private void HomeNavigationCommandHandler()
{
Messenger.Default.Send(new NavigateToPageMessage {PageName = "newpage"});
}
In the newpage Viewmodel,you should register the messenger like this,
Messenger.Default.Register<NavigateToPageMessage>(this, (action) => ReceiveMessage(action));
private object ReceiveMessage(NavigateToPageMessage action)
{
var page = string.Format("/Views/{0}.xaml", action.PageName);
NavigationService.Navigate(new System.Uri(page,System.UriKind.Relative));
return null;
}
//Assuming your views are in View Folder