How to update content of dialoghost in wpf material design? - c#

This error happen when I open new usercontrol into dialoghost during opening dialoghost :
when I press submit button
this is my code xaml:
MainWindow:
<Grid>
<Button Content="Show"
Command="{Binding OpenRegisCommand}"
VerticalAlignment="Bottom"
Margin="0 0 0 40"
Foreground="White"
Width="100"
Background="#23A9F2">
</Button>
<md:DialogHost Identifier="RootDialogHostId">
</md:DialogHost>
</Grid>
my usercontrol and my mainwindow using 1 viewmodel:
public MainViewModel()
{
OpenRegisCommand = new DelegateCommand(OpenFormRegis);
Submit = new DelegateCommand(Check);
}
private async void Check()
{
if(Name.Equals("admin")&&Pass.Equals("123456"))
{
var view = new DialogOk();
await DialogHost.Show(view, DialogHostId);
}
else
{
var view = new DialogNo();
await DialogHost.Show(view, DialogHostId);
}
}
private async void OpenFormRegis()
{
var view = new FormRegis();
await DialogHost.Show(view, DialogHostId);
}
button submit in my usercontrol binding to DelegateCommand Submit in my viewmodel

Each dialog hosts can be used to show one view at a time.
If you have two views that you wanted to shot at the same time (less likely), you will need two dialog hosts.
In your case, if you're trying to open the "OpenFormRegis" window in your dialog host, I would suggest you to use Windows instead.

In my case, I did the following:
1- Get the dialogs that are active, using DialogHost.GetDialogSession("RootDialog").
2- If it is different from null, set the content with the UpdateContent(alertBox) method which updates the content of the dialog.
3- If it is null, establish the usual flow to show the content of the dialog.
AlertBoxView alertBox = new AlertBoxView();
alertBox.DataContext = this;
var dialog = DialogHost.GetDialogSession(IdentifierDialog);
if (dialog != null)
{
dialog.UpdateContent(alertBox);
}
else
{
await DialogHost.Show(alertBox, IdentifierDialog);
}
*AlertBoxView: is my custom view of DialogHost
*IdentifierDialog: is the variable that takes the Identifier of the dialog

Related

How to show a Content Dialog with multiple input fields from viewmodel

I am building an C# UWP application where onclick of a button in the view a command (using System.Windows.Input.IComand) raises in viewmodel(not code behind) which builds a form of input fields like Name:____ phone:____ etc and shows it on the UI/view.
I dont want to keep the <ContentDialog></ContentDialog> in the
view.
What have I done so far is
Button in my view:
<CommandBar>
<AppBarButton x:Name="buttonNew" Command="{Binding AddClick}" />
</CommandBar>
ViewModel object in the code behind is set as the DataContext of the
View
In the view model:
public ICommand AddClick=> new RelayCommand(Add);
private async void Add()
{
TextBox input = new TextBox()
{
PlaceholderText = "Name",
};
var contentDialog = new ContentDialog
{
Title = "Add a Person",
Content = input,
FullSizeDesired = true,
PrimaryButtonText = "Add",
CloseButtonText = "Cancel"
};
await contentDialog.ShowAsync();
}
As you can see I am able to show/trigger a Content Dialog box with one input field and 2 buttons.
My problem is:
How to add more input fields like we add in a
<StackPanel></StackPanel> in the code of view model and assign it
to the content of the Content Dialog?
How to size it accordingly that all the fields show up properly and does not show up haphazard since am not coding this in the Xaml?
From the general application construction practice, it is recommended to create a UI using XAML to create a custom ContentDialog.
You don’t have to write the XAML code of ContentDialog in the View, you can add new item in Visual Studio, select the Content Dialog template, and create a custom dialog derived from ContentDialog.
Then use code similar to the following:
MyCustomDialog.xaml
<ContentDialog
...
Title="Add a Person"
FullSizeDesired="True"
PrimaryButtonText="Add"
CloseButtonText="Cancel"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<StackPanel>
<TextBox Header="Name" x:Name="NameBox" PlaceholderText="Name"
HorizontalAlignment="Stretch"/>
<TextBox Header="Phone" x:Name="PhoneBox" PlaceholderText="Phone"
Margin="0,15,0,0" HorizontalAlignment="Stretch"
InputScope="Number"/>
</StackPanel>
</ContentDialog>
ViewModel.cs
private async void Add()
{
var dialog = new MyCustomDialog();
await dialog.ShowAsync();
}
If you insist on using C# code to create ContentDialog, you need to convert the tags in XAML into corresponding classes, but this method is not easy to debug.
private async void Add()
{
var container = new StackPanel();
TextBox nameBox = new TextBox()
{
PlaceholderText = "Name",
Header = "Name",
HorizontalAlignment = HorizontalAlignment.Stretch
};
TextBox phoneBox = new TextBox()
{
PlaceholderText = "Phone",
Header = "Phone",
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 15, 0, 0)
};
container.Children.Add(nameBox);
container.Children.Add(phoneBox);
var contentDialog = new ContentDialog
{
Title = "Add a Person",
Content = container,
FullSizeDesired = true,
PrimaryButtonText = "Add",
CloseButtonText = "Cancel"
};
await contentDialog.ShowAsync();
}
The tags in XAML are actually the corresponding classes in C#, from the code you provided, the ContentDialog is displayed in full screen. You mentioned that you want the fields to be displayed correctly, if you mean that the string entered in the TextBox is too long and the text is not displayed completely, you can set the TextBox.TextWrapping property to True.

WPF Update UI From Background Thread

I know similar questions have been asked many times but I have not been able to find anything that works in this situation. I have an application that runs minimized to the taskbar using wpf-notifyicon. The main window opens, authenticates and is then hidden. A process runs in the background and I would like it to send updates to the main thread. Most of the time it works. However, the taskbar icon has a context menu that allows the user to open application settings. If I open then close the settings window, the next time I try to update the balloon, I get a a null reference error System.NullReferenceException: 'Object reference not set to an instance of an object.' System.Windows.Application.MainWindow.get returned null.
It's like once I open another window, the main window is lost and I can't figure out how to find it again.
This is how I am updating the balloon. This code is in a notification service and is called from inside the MainWindow View Model and from inside other services.
// Show balloon update on the main thread
Application.Current.Dispatcher.Invoke( new Action( () =>
{
var notifyIcon = ( TaskbarIcon )Application.Current.MainWindow.FindName( "NotifyIcon" );
notifyIcon.ShowBalloonTip( title, message, balloonIcon );
} ), DispatcherPriority.Normal );
The notification icon is declared inside the XAML for the main window.
<tb:TaskbarIcon
x:Name="NotifyIcon"
IconSource="/Resources/Icons/card_16x16.ico"
ToolTipText="Processor"
MenuActivation="LeftOrRightClick"
DoubleClickCommand="{Binding ShowStatusCommand}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Settings" Command="{Binding ShowSettingsCommand}" />
<Separator />
<MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
<tb:TaskbarIcon.TrayToolTip>
<Border Background="Gray"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="3"
Opacity="0.8"
Width="180"
Height="20">
<TextBlock Text="{Binding ListeningMessage }" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
How can I safely update the balloon icon from background threads?
Update 1:
The context menu is bound to commands in the view model. To open the settings window
<ContextMenu>
<MenuItem Header="Settings" Command="{Binding ShowSettingsCommand}" />
<Separator />
<MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}" />
</ContextMenu>
The command in the VM is:
public ICommand ShowSettingsCommand => new DelegateCommand
{
CommandAction = () =>
{
Application.Current.MainWindow = new Views.SettingsWindow( _logger, _hidservice, _certificateService );
Application.Current.MainWindow.Show();
}
};
To close the settings window, I have an action in the window code behind
public ICommand CancelSettingsCommand => new DelegateCommand
{
CommandAction = () => CloseAction()
};
// In Code Behind
vm.CloseAction = new Action( () => this.Close() );
You are overriden the main window. You should not do that. Just create a new instance of it and call Show().
public ICommand ShowSettingsCommand => new DelegateCommand
{
CommandAction = () =>
{
var settingsWindow = = new Views.SettingsWindow( _logger, _hidservice, _certificateService );
settingsWindow.Show();
}
};
Don't show the window from the view model. Use an event handler instead.
Also don't override the value of Application.MainWindow.
Don't use Dispatcher to show progress. Since .NET 4.5 the recommended pattern is to use IProgres<T>. The frameworks provides a default implementation Progress<T>:
Progress model to hold progress data
class ProgressArgs
{
public string Title { get; set; }
public string Message { get; set; }
public object Icon { get; set; }
}
Main UI thread
private void ShowSettings_OnMenuClicked(object sender, EventArgs e)
{
// Pass this IProgress<T> instance to every class/thread
// that needs to report progress (execute the delegate)
IProgres<ProgressArgs> progressReporter = new Progress<ProgressArgs>(ReportPropgressDelegate);
var settingsWindow = new Views.SettingsWindow(_logger, _hidservice, _certificateService, progressReporter);
}
private void ReportPropgressDelegate(ProgressArgs progress)
{
var notifyIcon = (TaskbarIcon) Application.Current.MainWindow.FindName("NotifyIcon");
notifyIcon.ShowBalloonTip(progress.Title, progress.Message, progress.Icon);
}
Background thread
private void DoWork(IProgress<ProgressArgs> progressReporter)
{
// Do work
// Report progress
var progress = new ProgressArgs() { Title = "Title", Message = "Some message", Icon = null };
progressReporter.Report(progress);
}

Alert Box does not appear in xamarin Forms, DisplayAlert()

I am trying to display to alert box after tapping or click on the selected ListView box. Currently the selectedItem is binding to SelectedTicket, which trigger an object call SelectedTicket within the ViewModel. Once the SelectedTicket is triggered, it then executes a popup.show() method with the DisplayAlert() method. The DisplayAlert() method get executes but does not display the AlertBox.
==============================================
////Xaml Page
<ListView ItemsSource="{Binding TicketList,Mode=OneWay}" RowHeight="130"
ItemTemplate="{StaticResource TicketListTileTemplate}" BackgroundColor="#d9deeb"
SelectedItem="{Binding SelectedTicket, Mode=TwoWay}" SeparatorVisibility="None">
</ListView>
==================================================
////ViewModal
public object SelectedTicket
{
get
{
return _SelectedTicket;
}
set
{
if (SetProperty(ref _SelectedTicket, value))
{
if ((value != null) && (value is TicketListItem))
{
popup1.Show();
SelectedTicket = null;
}
}
}
}
======================================================
////Popup.cs
async public void Show()
{
DisplayAlert("Alert", "Your have been alerted", "OK");
}
One of the most common reasons I don't see a DisplayAlert is because it is being called on a Page that isn't active on screen.
As a quick workaround/test you can do
await Application.Current.MainPage.DisplayAlert("Alert", "Your have been alerted", "OK");
If this works, my first assumption is confirmed.
I always try to keep my code behind clean, hence calling from the ViewModel is certainly a good approach. Normally your MVVM Library has some code to help with display alerts.
DisplayAlert() is only available to Page objects (such as ContentPage or NavigationPage) see here, your Popup.cs might not be a Page object. Also you are not awaiting DisplayAlert which you always want to do with async methods. Finally, your Show() method may not be running on the UI thread.
Instead of trying to show the alert from your ViewModel, why don't you try displaying the alert from the code-behind of your XAML page like this:
XAML:
<ListView ItemsSource="{Binding TicketList,Mode=OneWay}"
RowHeight="130"
ItemTemplate="{StaticResource TicketListTileTemplate}" BackgroundColor="#d9deeb"
SelectedItem="{Binding SelectedTicket, Mode=TwoWay}"
SeparatorVisibility="None"
ItemSelected="OnItemTapped"> <!-- Notice ItemTapped here will trigger when an item is tapped, imagine that -->
In Code-behind:
....
private TicketViewModel _viewModel = new TicketViewModel();
....
public async void OnItemTapped (object o, ItemTappedEventArgs e) { //Notice I added 'async' to the method here, that is so we can 'await' the DisplayAlert below (ALWAYS 'await' the DisplayAlert and ANY other async methods)
TicketListItem item = (TicketListItem)o;
if (item != null) {
await DisplayAlert("Alert", "Your have been alerted", "OK"); //Notice the 'await' here
_viewModel.SelectedTicket = null;
}
}

MVVM, DialogService and Dialog Result

I'm currently learning WPF/MVVM, and have been using the code in the following question to display dialogs using a Dialog Service (including the boolean change from Julian Dominguez):
Good or bad practice for Dialogs in wpf with MVVM?
Displaying a dialog works well, but the dialog result is always false despite the fact that the dialog is actually being shown. My DialogViewModel is currently empty, and I think that maybe I need to "hook up" my DialogViewModel to the RequestCloseDialog event. Is this the case?
does your DialogViewmodel implement IDialogResultVMHelper? and does your View/DataTemplate has a Command Binding to your DialogViewmodel which raise the RequestCloseDialog?
eg
public class DialogViewmodel : INPCBase, IDialogResultVMHelper
{
private readonly Lazy<DelegateCommand> _acceptCommand;
public DialogViewmodel()
{
this._acceptCommand = new Lazy<DelegateCommand>(() => new DelegateCommand(() => InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(true)), () => **Your Condition goes here**));
}
public event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
private void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e)
{
var handler = RequestCloseDialog;
if (handler != null)
handler(this, e);
}
}
anywhere in your Dialog control:
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" MinHeight="30">
<Button IsDefault="True" Content="Übernehmen" MinWidth="100" Command="{Binding AcceptCommand}"/>
<Button IsCancel="True" Content="Abbrechen" MinWidth="100"/>
</StackPanel>
and then your result should work in your viewmodel
var dialog = new DialogViewmodel();
var result = _dialogservice.ShowDialog("My Dialog", dialog );
if(result.HasValue && result.Value)
{
//accept true
}
else
{
//Cancel or false
}

Passing application state between viewmodels in MVVM WPF application

I need to write a small application to read a configuration file and generate some report with it. I was hoping to finally use MVVM but it's quite tricky to get started. Oh, I'm using Caliburn.Micro framework.
So this is what I have, a shell (primary view that hosts other views) that has a ribbon with 3 buttons on it:
1) Open file
2) Show settings
3) Show results
And two other views, SettingsView and ResultsView with buttons to generate and delete a report.
So I guess the view structure would be like this:
ShellView
Ribbon
OpenFileButton
SettingsButton
ResultsButton
ContentControl (hosts SettingsView and ResultsView)
SettingsView
CalculateResultsButton
ResultsView
CancelResultsButton
The tricky part is this:
1. "Show settings" button is disabled until a file is opened (via Open file).
2. "Show results" button is disabled until a report is calculated (via a
method in SettingsViewModel).
3. If a report is calculated, the CalculateResultsButton is disabled and
CancelResultsButton is enabled and vice versa.
Please advise how could I achieve this ? I've no ideas what strategy should I go for. My non-MVVM-thinking-brain says that I should create a status variable and then somehow bind those buttons to that variable, but I guess that wont work in a MVVM world, right ? Any code example would be very very very appreciated!
Many thanks!
Since you're using CM you won't need any code-behind. You can delete the .xaml.cs files if you want.
This is a pretty basic example but it should give you an idea on how to control the state of the buttons. In this example, Open will be enabled and the other two are disabled. If you click on Open, Settings is enabled. The same happens with Results once Settings is clicked.
If you need a way to do global state the same concept can be applied by injecting a singleton, SharedViewModel, into the ViewModels and the CanXXX methods can check values in SharedViewModel. This is a SL demo of different things but one is injecting a singleton to share data, the same idea applies in wpf.
ShellView:
<Window x:Class="CMWPFGuardSample.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal">
<Button x:Name="Open"
Content="Open" />
<Button x:Name="Settings"
Content="Settings" />
<Button x:Name="Results"
Content="Results" />
</StackPanel>
</Grid>
</Window>
ShellViewModel:
[Export(typeof (IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
private bool _isOpen;
public bool IsOpen
{
get { return _isOpen; }
set
{
_isOpen = value;
NotifyOfPropertyChange(() => IsOpen);
NotifyOfPropertyChange(() => CanSettings);
}
}
private bool _isSettings;
public bool IsSettings
{
get { return _isSettings; }
set
{
_isSettings = value;
NotifyOfPropertyChange(() => IsSettings);
NotifyOfPropertyChange(() => CanResults);
}
}
public bool IsResults { get; set; }
public void Open()
{
IsOpen = true;
}
public bool CanSettings
{
get { return IsOpen; }
}
public void Settings()
{
IsSettings = true;
}
public bool CanResults
{
get { return IsSettings; }
}
public void Results()
{
}
}
MVVM and WPF Commands perfectly fits your "tricky part" requirements since have built in ICommand.CanExecute() method which allows enabling/disabling corresponding button based on custom logic.
To use this naice feature take a look first at the RoutedCommand Class and self explanatory example on MSDN How to: Enable a Command (see below code snippets).
And in general about MVVM, it is really SIMPLE! Just try it and you won't leave without it ;) In few words - you have to create for each EntityView.xaml corresponding EntityViewModel class and then just put instance of it in the View's DataContext either explicitly in code or using bindings:
var entityViewModel = new EntityViewModel();
var view = new EntityView();
view.DataContext = entityViewModel;
MVVM Command and Command.CanExecute bindings:
XAML:
<Window x:Class="WCSamples.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CloseCommand"
Name="RootWindow"
>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="CloseCommandHandler"
CanExecute="CanExecuteHandler"
/>
</Window.CommandBindings>
<StackPanel Name="MainStackPanel">
<Button Command="ApplicationCommands.Close"
Content="Close File" />
</StackPanel>
</Window>
C# code behind:
// Create ui elements.
StackPanel CloseCmdStackPanel = new StackPanel();
Button CloseCmdButton = new Button();
CloseCmdStackPanel.Children.Add(CloseCmdButton);
// Set Button's properties.
CloseCmdButton.Content = "Close File";
CloseCmdButton.Command = ApplicationCommands.Close;
// Create the CommandBinding.
CommandBinding CloseCommandBinding = new CommandBinding(
ApplicationCommands.Close, CloseCommandHandler, CanExecuteHandler);
// Add the CommandBinding to the root Window.
RootWindow.CommandBindings.Add(CloseCommandBinding);

Categories