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;
}
}
Related
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
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
}
I have two windows one to add users and the other to view all the users, both of them are connected the database. What I want is to show the user automatically in the view all windows after I press the add button in the add users window (before closing it).
In view all window (I am posing the data binding parts only):-
Note:
The following code is in the constructor of MainWindows.xaml.
Basically the ProfileControl is a custom control, profileList is
ListBox and profileCollection is an ObservableCollection
if (dbObj.GetDbData().Count != 0)
{
foreach (ProfileControl item in dbObj.GetDbData())
{
profileCollection.Add(item);
}
this.profileList.DataContext = profileCollection;
}
The following is for the ListBox.
<ListBox Name="profileList" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<profileControl:ProfileControl Name="pcList" onClickUser="ProfileControl_onClickUser" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
And lastly the following is the add button which is in a AddUser.xaml.
Note: Basically I am checking first that the user I am adding is check-in or not. If not I am adding it to the database.
private void addButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (co.checkIfCheckedIn(memoryCard))
{
MessageBox.Show("The person is inside. ", "No Adding", MessageBoxButton.OK, MessageBoxImage.Stop);
}
else
{
co.AddData(memoryCard, emailTb.Text, contactNumberTb.Text, workPlaceTb.Text, infoTb.Text);
MessageBox.Show("Saving is done. ", "Saved", MessageBoxButton.OK, MessageBoxImage.Asterisk);
this.notificationTB.Text = " Saving is complete.";
}
}
catch (Exception ex)
{
this.notificationTB.Text = ex.Message;
this.notificationTB.Foreground = (Brush)new BrushConverter().ConvertFromString("White");
this.notificationTB.Background = (Brush)new BrushConverter().ConvertFromString("Red");
}
}
I want to add that, it should after I save the data by the add button shows the user in the MainWindows automatically, but it not. And I have to close the application and open it again to view the data. I believe I need to access the ObservableCollection object profileCollection in the MainWindows.xaml to do this but I am not sure if it is right and how.
Please advise me, thank.
There is at least 3 solutions from the simplest (bad from a dogmatic "decouple them all" POV but can be pragmatic) to the mass plumbing (good from a design POV):
1) share your collections via a global resource in Application.Current.Resources:
Application.Current.Resources["MyUserCollection"] = theUserCollection;
2) use a ViewModel shared by both views, exposing the users collection; you'll typically set it as the DataContext of both views
3) use a message broker to inform the main view that a new user was added, you'll need some MVVM framework for that like MVVM Light or Caliburn Micro
EDIT: here is another solution which is a good compromise:
4) in your MainWindow code-behind expose a Refresh/Reload method:
public void ReloadUsers()
{
profileCollection.Clear();
var data = dbObj.GetDbData();
if (data.Count != 0)
{
foreach (ProfileControl item in data)
{
profileCollection.Add(item);
}
this.profileList.DataContext = profileCollection;
}
}
And call it from your other Window:
private void addButton_Click(object sender, RoutedEventArgs e)
{
try
{
...
(Application.Current.MainWindow as MainWindow).ReloadUsers();
}
catch (Exception ex)
{
...
}
}
I am trying to implement localisation in my wp8 app.
I have two radio buttons (Arabic and English) when a user selects one of the radio button then the pop up appears that is he sure he want to change language of the app if he chooses ok then the language of the app changes but if he chooses cancel then there will be no change in the language but the issue is that the radio button toggles even if user presses cancel.
How to stop the toggle to happen ?
Here is my Xaml code and the code which is binded if any radio button is checked ..
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20">
<RadioButton Content="English" Foreground="Black" FontSize="30" IsChecked="{Binding isenglishchecked,Mode=TwoWay, Source={StaticResource appSettings}}" Checked="RadioButton_Checked"/>
<RadioButton Content="Arabic" Foreground="Black" FontSize="30" IsChecked="{Binding isarabicchecked,Mode=TwoWay, Source={StaticResource appSettings}}" Checked="RadioButton_Checked"/>
</StackPanel>
</Grid>
public bool isenglishchecked
{
get
{
return GetValueOrDefault<bool>(isenglishcheckedname, isenglishcheckedDefault);
}
set
{
var result = MessageBox.Show("This Will Change the Language of the App Do you Want to continue ?", "Language Change", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
if (AddOrUpdateValue(isenglishcheckedname, value))
{
Save();
if (isenglishchecked)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
MainViewModel.stationnamelist = null;
Messenger.Default.Send<string>("mainpage", "tomainpage");
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
else
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("ar-AE");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("ar-AE");
MainViewModel.stationnamelist = null;
Messenger.Default.Send<string>("mainpage", "tomainpage");
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
}
else
{
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/Skins/SelectLanguage.xaml", UriKind.Relative));
}
}
}
public bool isarabicchecked
{
get
{
return GetValueOrDefault<bool>(isarabiccheckedname, isarabiccheckedDefault);
}
set
{
if (AddOrUpdateValue(isarabiccheckedname, value))
{
Save();
}
}
}
What's likely happening is that the UI layer (Silverlight), in response to the user's tap, happily changes its own state, and then sets the value of isenglishchecked or isarabicchecked. The fact that the value of the property doesn't change is of no consequence -- Silverlight doesn't notice. (not changing the value of the property in response to a set call is non-standard behavior, so it's not surprising that Silverlight would "misbehave" like this)
There are a few things you can try:
In the set handler for both properties, raise the PropertyChanged event (in INotifyPropertyChanged) for isenglishchecked and isarabicchecked. This will cause the UI to re-query the view model so it's back in sync with it. If the checked properties didn't change, the UI will notice this.
Use the built-in ExceptionValidationRule on each RadioButton, then throw an exception in the Set handler if the user cancels the operation. This might cause the radio button's value not to change (good), but it might also make your button red.
Define your own ValidationRule. It might work better to show the message box during ValidationRule.Validate.
Ditch the confirmation dialog entirely and just change the language immediately. If it was a mistake, the user can just tap the other button.
I have a page in a Windows Phone 7 app where the user can edit or delete an Transaction object. The Transaction object is an Linq-to-Sql class that have a relationship with the Account class and the Category class. In the page, I use a ListPicker to let the user select the account and category for the given transaction, like this:
<toolkit:ListPicker Grid.Row="1" FullModeHeader="Choose the Account" FullModeItemTemplate="{StaticResource FullModeItemTemplate}" ExpansionMode="FullScreenOnly" Background="#26000000" Margin="10,0,10,0" Name="Account" SelectedItem="{Binding Account, Mode=TwoWay}" Tap="ListPicker_Tap" />
<toolkit:ListPicker Grid.Row="7" FullModeHeader="Choose the Category" FullModeItemTemplate="{StaticResource FullModeItemTemplate}" ExpansionMode="FullScreenOnly" Background="#26000000" Margin="10,0,10,0" Name="Category" SelectedItem="{Binding Category, Mode=TwoWay}" Tap="ListPicker_Tap" />
The ListPicker_Tap event is a fix for a bug in the Aug/2011 version of the WPF Toolkit for Windows Phone and is simply this:
private void ListPicker_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
ListPicker lp = (ListPicker)sender;
lp.Open();
}
If the user edit the transaction, everything is fine, but if the user try to delete it, I get an error saying that "SelectedItem must always be set to a valid value".
Here's the code if the user click in the delete button in the appbar in the TransactionPage.xaml.cs:
private void appBarDelete_Click(object sender, EventArgs e)
{
MessageBoxResult result = MessageBox.Show("Are you sure?\n", "Confirm", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
App.ViewModel.DeleteTransaction(transaction);
}
NavigationService.GoBack();
}
My ViewModel.DeleteTransaction method:
public void DeleteTransaction(Transaction transaction)
{
AllTransactions.Remove(transaction);
transactionRepository.Delete(transaction);
}
My transactionRepository.Delete method:
public void Delete(Transaction transaction)
{
Context.Transactions.DeleteOnSubmit(transaction);
Context.SubmitChanges();
}
I receive the error in the Context.SubmitChanges() execution, the debug points to the NotifyPropertyChanged inside the Transaction class, the line where I get the error is this:
protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In the propertyName attribute the value is "Category". It looks like when deleting the object send the propertychanged event of category and accounts, and because the listpicker is in the TwoWay mode, it have some trouble dealing with it. How could I fix it? I need some help.
This error may also be caused by the order of the XAML properties:
This does NOT work (throws the exception because the ItemsSource is null when the SelectedItem is set):
<toolkit:ListPicker DisplayMemberPath="Title" SelectionMode="Single"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
ItemsSource="{Binding Categories}" />
This works as the itemssource is initialized first:
<toolkit:ListPicker DisplayMemberPath="Title" SelectionMode="Single"
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}" />
ListPicker uses Items.IndexOf to get the index of item instance that it should select.
If the instance does not match (it is not an object instance from the collection) the IndexOf will return -1 and the InvalidOperationException is thrown with the message: "SelectedItem must always be set to a valid value".
Override Equals method of the item type in the collection and it will work as expected.
Example:
public override bool Equals(object obj)
{
var target = obj as ThisType;
if (target == null)
return false;
if (this.ID == target.ID)
return true;
return false;
}
Hope it helps
There are just two checks which throws the InvalidOperationException on SelectedItem
Listpicker Items is null
(Declarative: Order of attributes matters. if selecteditem must appear after itemsource
(Programatic: make sure itemsource is loaded)
Listpicker applies Indexof on Items to set selected item. So make sure you override Equals if necessary.
Debugging with watch on listpicker.Items and overridden Equals method will help us identify issue
The problem is that the ListPicker is expecting the SelectedItem to be a ListPickerItem whereas you're binding it to an object of type Transaction. You can get around the problem by binding to the SelectedIndex property instead and then select the appropriate object from your ViewModel based on the index.
Also, if the reason you have the Tap handler defined is because of the bug where the ListPicker does not open when placed within a ScrollViewer, take a look at patch ID 10247. If you recompile the toolkit with that patch it fixes the problem.