I am doing an application in MVVM and I am new at it...
I have a boolean field and want to show a combobox to user with items Yes/No but when user selects it, but in data context values are 1 and 0.
i have the following code:
<TextBlock Grid.Row="2" Grid.Column="2" Text="Batch Flag" Margin="5,0,0,0" />
<ComboBox Grid.Row="2" Grid.Column="3" x:Name="cboBtchFlg" SelectedItem="{Binding SelectedADM_M022.BtchFlg,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="5,0,0,2" Background="Transparent">
<ComboBoxItem Tag="1">True</ComboBoxItem>
<ComboBoxItem Tag="0">False</ComboBoxItem>
</ComboBox>
You could use a converter. If the view model property is a bool, and this is bound to the SelectedIndex property of your combobox, which is an int, then this example will provide what you need.
public class IntToBoolConverter : IValueConverter
{
// from view model to view
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
bool trueFalse = (bool)value;
return trueFalse == true ? 0 : 1;
}
return value;
}
// from view to model
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is int)
{
int index = (int)value;
if (index == 0)
return true;
if (index == 1)
return false;
}
return value;
}
}
Amend the SelectedIndex binding to
SelectedItem="{Binding SelectedADM_M022.BtchFlg,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, Converter={StaticResource boolConverter}}"
given you have a resource called boolConverter that references your converter class e,g,
<Window.Resources>
<local:IntToBoolConverter x:Key="boolConverter" />
</Window.Resources>
Related
I have a ListView shown below.
How can I bind dictionary to ListView Itemsource so that my label as key and ENtry has value?
I don't know How to proceed further
I tried with this but I am getting null reference exception
<ListView x:Name="ItemsListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="true" ItemsSource="{Binding dictionary}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
view model
public List<string> Key
{
get { return key; }
set
{
SetProperty(ref key, value);
}
}
public List<Int32> Value
{
get { return val; }
set
{
SetProperty(ref val, value);
}
}**
for (int i = 0; i < AllProductsList.Count; i++)
{
Value.Add(0);
//Value = new ObservableCollection<Int32>(val);
}
for (int j = 0; j < AllProductsList.Count; j++)
{
for (int k = 0; k < Value.Count; k++)
{
if (j == k)
{
dictionary[Key[j]] = Value[k];
}
}
If the ItemSource is a Dictionary, then simply Binding "Key" and "Value" should work. I guess that is what you did. But You don't need to create properties "Key" and "Value". So please remove that..
//Remove these Properties
public List<string> Key
{
get { return key; }
set
{
SetProperty(ref key, value);
}
}
public List<Int32> Value
{
get { return val; }
set
{
SetProperty(ref val, value);
}
}**
What you did in your Xaml is correct.
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
Label will show the Keys and Entry will show the value. Now, make the ItemSource of your List Binding your Dictionary(instead of the IList/List).
If you set the ItemSource= "{Binding YourDictionary}", then you can bind Key and Value as you did(Provided, YourDictionary is of type Dictionary<string,string>).
Because not knowing what type of your source data,if source data is a json type from web api, you can refer to this discussion to convert json object to ViewMidel.
In ListView ,ItemSource can be used as follow:
DictionaryModel.cs:
public class DictionaryModel : INotifyPropertyChanged
{
string key= string.Empty;
public string Key
{
get { return key; }
set { SetProperty(ref key, value); }
}
Int32 valueint = 0;
public Int32 Value
{
get { return valueint; }
set { SetProperty(ref valueint, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
ViewModel.cs:
public class ViewModel
{
public IList<DictionaryModel> DictionaryModels { get; private set; }
public ViewModel()
{
DictionaryModels = new List<DictionaryModel>();
// set demo data
DictionaryModels.Add(new DictionaryModel
{
Key = "Baboon",
Value= 1,
});
DictionaryModels.Add(new DictionaryModel
{
Key = "Capuchin",
Value= 2,
});
}
}
Then in ContenPage.cs , binding ViewModel:
BindingContext = new ViewModel();
Finally in Xaml :
<ListView x:Name="ItemsListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="true" ItemsSource="{Binding DictionaryModels}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm developing a UWP app and I'm facing a problem. The app uses the MVVM pattern with Template10. I have created a similar solution that recreates the problem that I'm facing. In that solution, a list of orders are displayed, the user chooses an order and then click the "Edit" button. Then a second page is displayed and pre-loaded with the previous selected order, in this second page the user can edit the order. The problem is in the second page, the data bound to comboboxes doesn't show. Maybe the problem is related to this question. In my case, the SelectedValue is set before the ItemsSource. After debugging, I have reached these lines of code in OrderEditionPage.g.cs:
private void Update_ViewModel(global::ComboApp.ViewModels.OrderEditionPageViewModel obj, int phase)
{
this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
}
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{
this.Update_ViewModel_BusinessAssociates(obj.BusinessAssociates, phase);
this.Update_ViewModel_TransactionTypes(obj.TransactionTypes, phase);
this.Update_ViewModel_OrderTypes(obj.OrderTypes, phase);
this.Update_ViewModel_ShowSelectedOrder(obj.ShowSelectedOrder, phase);
}
}
}
If I could achieve this line of code be executed at last, my problem would be solved: this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
How could I achieve this? How does Visual Studio determine the order of this lines?
OrderEditionPage.xaml
<Page
x:Class="ComboApp.Views.OrderEditionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:myconverters="using:ComboApp.Converters"
xmlns:t10converters="using:Template10.Converters"
mc:Ignorable="d">
<Page.Resources>
<t10converters:ChangeTypeConverter x:Key="TypeConverter" />
<myconverters:DateTimeConverter x:Key="DateTimeConverter" />
</Page.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel
Padding="15, 5"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox
Header="Order #"
Margin="5"
Width="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.ExternalId, Mode=TwoWay}" />
<ComboBox
Header="Business Associate"
Margin="5"
MinWidth="300"
SelectedValuePath="BusinessAssociateId"
DisplayMemberPath="Name1"
ItemsSource="{x:Bind ViewModel.BusinessAssociates}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.BusinessAssociateId, Mode=TwoWay, Converter={StaticResource TypeConverter}}" />
<DatePicker
Header="Delivery Date"
Margin="5"
MinWidth="0"
Width="200"
Date="{x:Bind ViewModel.SelectedOrder.DeliveryDate, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}" />
<ComboBox
Header="Transaction"
MinWidth="200"
Margin="5"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.TransactionTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.TransactionType, Mode=TwoWay}" />
<TextBox
Header="Priority"
Margin="5"
MaxWidth="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.Priority}" />
<ComboBox
Header="Type"
Margin="5"
MinWidth="200"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.OrderTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.OrderType, Mode=TwoWay}" />
<TextBox
Header="Information"
Margin="5"
Height="100"
AcceptsReturn="True"
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{x:Bind ViewModel.SelectedOrder.Information, Mode=TwoWay}" />
<Button
Margin="5"
Content="Show"
Width="100"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.ShowSelectedOrder}" />
</StackPanel>
</ScrollViewer>
</Page>
OrderEditionPage.xaml.cs
using ComboApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace ComboApp.Views
{
public sealed partial class OrderEditionPage : Page
{
public OrderEditionPageViewModel ViewModel => DataContext as OrderEditionPageViewModel;
public OrderEditionPage()
{
this.InitializeComponent();
}
}
}
OrderEditionPageViewModel.cs
using ComboApp.Models;
using ComboApp.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Template10.Mvvm;
using Template10.Utils;
using Windows.UI.Xaml.Navigation;
namespace ComboApp.ViewModels
{
public class OrderEditionPageViewModel
: ViewModelBase
{
private IBusinessAssociateService businessAssociateService;
private Order selectedOrder;
public Order SelectedOrder
{
get { return selectedOrder; }
set { Set(ref selectedOrder, value); }
}
public ObservableCollection<object> TransactionTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<object> OrderTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<BusinessAssociate> BusinessAssociates { get; set; } = new ObservableCollection<BusinessAssociate>();
public OrderEditionPageViewModel(IBusinessAssociateService businessAssociateService)
{
this.businessAssociateService = businessAssociateService;
TransactionTypes.Add(new { Value = "I", Display = "Incoming" });
TransactionTypes.Add(new { Value = "O", Display = "Outgoing" });
TransactionTypes.Add(new { Value = "T", Display = "Transfer" });
OrderTypes.Add(new { Value = "M", Display = "Manual" });
OrderTypes.Add(new { Value = "A", Display = "Automatic" });
OrderTypes.Add(new { Value = "S", Display = "Semi-automatic" });
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
// Loading buiness associates
var response = await businessAssociateService.GetNextPageAsync();
if (response.IsSuccessful)
{
BusinessAssociates.AddRange(response.Result.Items);
}
SelectedOrder = (Order)parameter;
await base.OnNavigatedToAsync(parameter, mode, state);
}
private DelegateCommand showSelectedOrder;
public DelegateCommand ShowSelectedOrder => showSelectedOrder ?? (showSelectedOrder = new DelegateCommand(async () =>
{
await Views.MessageBox.ShowAsync(JsonConvert.SerializeObject(SelectedOrder, Formatting.Indented));
}));
}
}
It is a known issue of x:Bind when the SelectedValue of a ComboBox is sometimes set before its ItemsSource, you can read more about it here.
As a workaround you can use Bindings instead of x:Bind, but make sure that ItemsSource binding is placed before SelectedValue binding in XAML.
Alternatively you can try calling Bindings.Update() in the Page_Loaded event of your second page.
In my main page, i have a listview of all the items, and once user clicks on one of them, it will navigate to a detail page.
In the detail page, i create a last button to jump to last item,
<Button Content="Last" HorizontalAlignment="Center" >
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click">
<Interactions:InvokeCommandAction Command="{x:Bind Path=ViewModel.LastCommand, Mode=OneWay}"/>
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
and here is part of my viewmodel for the page
class DetailPageViewModel : ViewModelBase
{
private MyItem item;
public MyItem Item
{
get { return item; }
set { SetProperty(ref item, value); }
}
public DetailPageViewModel()
{
LastCommand = new DelegateCommand(LastItemExecute, CanLastItemExecute);
LastCommand.ObservesProperty(() => Item);
}
private DelegateCommand lastCommand;
public DelegateCommand LastCommand
{
get { return lastCommand; }
set { SetProperty(ref lastCommand, value); }
}
private bool CanLastItemExecute()
{
if (Item.Index!= 1)
{
return true;
}
else
{
return false;
}
}
private void LastItemExecute()
{
Item= _context.Items.Single(p => p.Index== Item.Index- 1);
}
}
Everything works fine here, except that if i click on the first item in the listview, or jump from second item, the last button will not be disabled, click on it won't do anything though.
But i would like to disable the button if the detail page is showing the first item, any help plz?
But i would like to disable the button if the detail page is showing the first item.
Your "Last Button" is in the DetailPage, I don't know how you bind data to controls in this DetaiPage, but if you are using Mater/Detail pattern, you can refer to the official Master/detail sample, in the ItemViewModel model, there is a property Item_ID. My suggestion is that you can also add a ID properity into your data model for ListView in the Master page, so can the ID be passed within the selected Item to detail page after navigation.
Then in the DetailPageViewModel, you can get this ID and bind to the Button's IsEnable property with Converter:
<Page.Resources>
<local:ButtonEnableConverter x:Key="cvt" />
</Page.Resources>
...
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Content="Last Item" Grid.Row="3"
IsEnabled="{x:Bind Item.ID, Mode=OneWay, Converter={StaticResource cvt}}">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Click">
<Core:InvokeCommandAction Command="{Binding Path=LastCommand, Mode=OneWay}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
The code of ButtonEnableConverter is like this:
public class ButtonEnableConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var itemID = (int)value;
if (itemID != 0)
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Update:
I didn't wrote a full sample to test it, but it should be something like this:
<Button Content="Last Item" Grid.Row="1" HorizontalAlignment="Center" >
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding Item.ID, Mode=OneWay}" ComparisonCondition="NotEqual" Value="100">
<Core:ChangePropertyAction PropertyName="IsEnabled" Value="True"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding Item.ID, Mode=OneWay}" ComparisonCondition="Equal" Value="100">
<Core:ChangePropertyAction PropertyName="IsEnabled" Value="False"/>
</Core:DataTriggerBehavior>
<Core:EventTriggerBehavior EventName="Click">
<Core:InvokeCommandAction Command="{Binding Path=LastCommand, Mode=OneWay}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
You can have a try.
I use AutoCompleteBox in MVVM and i want to execute something only if the user click on the Item or if the user press Enter.
But now when I use the down\Up Key on the keyboard the selectedItem property changes...
My controls :
<Controls:AutoCompleteBox ItemsSource="{Binding IndicationDtos, Mode=TwoWay}"
Width="100" SelectedItem="{Binding IndicationSelected, Mode=TwoWay}"
ValueMemberPath="Diagnosis" Text="{Binding Criteria, Mode=TwoWay}" MinimumPopulateDelay="250"/>
What can I do to make the property "SelectedItem" is assigned only on Enter or click?
If you have any question...
thanks a lot
In your SelectedItem binding, you can use:
SelectedItem="{Binding IndicationSelected, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
That way selected item only changes when you focus on something else
I found solution i created new class.
Like this :
public class AutoCompleteBoxEx : AutoCompleteBox
{
public static readonly DependencyProperty SelectionBoxItemProperty =
DependencyProperty.Register(
"SelectionBoxItem",
typeof(object),
typeof(AutoCompleteBox),
new PropertyMetadata(OnSelectionBoxItemPropertyChanged));
public object SelectionBoxItem
{
get
{
return GetValue(SelectionBoxItemProperty);
}
set
{
SetValue(SelectionBoxItemProperty, value);
}
}
protected override void OnDropDownClosing(RoutedPropertyChangingEventArgs<bool> e)
{
base.OnDropDownClosing(e);
SelectionBoxItem = SelectionAdapter.SelectedItem;
}
private static void OnSelectionBoxItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
Model
public class SearchParametersModel : ViewModelBase
{
private string _fromDate;
public string FromDate
{
get { return _fromDate; }
set
{
_fromDate = value;
VerifyPropertyName("FromDate");
}
}
private string _toDate;
public string ToDate
{
get { return _toDate; }
set
{
_toDate = value;
VerifyPropertyName("ToDate");
}
}
private DateTime? _selectedFromdate;
public DateTime? SelectedFromDate
{
get { return _selectedFromdate; }
set
{
_selectedFromdate = value;
VerifyPropertyName("SelectedFromDate");
}
}
private DateTime? _selectedTodate;
public DateTime? SelectedToDate
{
get { return _selectedTodate; }
set
{
_selectedTodate = value;
VerifyPropertyName("SelectedToDate");
}
}
}
ViewModel
private void Clear()
{
try
{
SearchParametersMdl.ToDate = string.Empty;
SearchParametersMdl.FromDate = string.Empty;
SearchParametersMdl.SelectedFromDate = null;
SearchParametersMdl.SelectedToDate = null;
}
catch (Exception ex)
{
throw ex;
}
}
View
<DatePicker Height="25" HorizontalAlignment="Left" Margin="84,71,0,0"
Name="dtpFromDate" VerticalAlignment="Top" Width="115" Text="{Binding Path=
SearchParametersMdl.FromDate,Mode=TwoWay}" SelectedDate="{Binding
Path=SearchParametersMdl.SelectedFromDate,Mode=TwoWay}" />
<DatePicker Height="25" HorizontalAlignment="Right" Margin="0,70,481,0"
Name="dtpToDate" VerticalAlignment="Top" Width="115" Text="{Binding Path=
SearchParametersMdl.ToDate,Mode=TwoWay}" SelectedDate="{Binding
Path=SearchParametersMdl.SelectedToDate ,Mode=TwoWay}" />
Unable to clear the values in datepicker control.
I want to display default value(i.e. Select a date) after firing clear cammand.
we can clear date by using the converter class:
in my application i am using the bellow convertor for clearing the date picker value
please find the bellow code snippet it may help you....
internal class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string date = ((DateTime)value).ToShortDateString();
if (!date.Equals("1/1/1753") && !date.Equals("1/1/0001"))
return ((DateTime)value).ToShortDateString();
return String.Empty;
}
/// <summary>
/// method for convert back
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string strValue = value == null ? string.Empty : value.ToString();
DateTime resultDateTime;
if (!string.IsNullOrEmpty(strValue))
return DateTime.TryParse(strValue, out resultDateTime) ? resultDateTime : value;
else
return new DateTime(1753, 1, 1);
}
}
i am using this in view as bellow:
<UserControl.Resources>
<local:DateConverter x:Key="converter"/>
</UserControl.Resources>
here local: namesapce for the convertor class
<DatePicker x:Name="date_DueDate" FontSize="9" Visibility="{Binding IsDueDateVisible}" SelectedDate="{Binding *ViewModelProperty*, Converter={StaticResource converter}, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True}" SelectedDateFormat="Short" Margin="5,5,0,0" VerticalAlignment="Top" MinWidth="100" MaxWidth="100" TabIndex="41"/>
You should remove the binding for the Text property that is probably messing up with the binding process. SelectedDate should suffice.
Especially when you are binding to a string property the binding doesn't know what to bind to convert null to unless you specify the TargetNullValue in the binding.