I am using MVVM pattern and I have LongListSelector in my page but I am not sure how to do these bindings:
I want in each row a button which do something with object in that row. I have this prepare in ViewModel:
private RelayCommand<Module> goToTrackingPageCommand;
public RelayCommand<Module> GoToTrackingPageCommand
{
get
{
return goToTrackingPageCommand
?? (goToTrackingPageCommand = new RelayCommand<Module>(
NavigateToTrackingPage));
}
}
private void NavigateToTrackingPage(Module module)
{
App.Current.SelectedModule = module;
navigationService.NavigateTo(new Uri("/Views/ModuleTrackingPage.xaml"), UriKind.Relative);
}
And I am trying to bind it like this:
<Button x:Name="ShowButton" Content="Show"
Command="{Binding GoToTrackingPageCommand}"
CommandParameter="{Binding}"/>
It's not working because button is in datatemplate and when there is binding it goes to selected Module object but not to ViewModel. So my first question is how can I fix it?
Second one is little complicated I guess but hope both get easy solution. I want have in each row ToggleSwitch too and when value is changed I want to call http request. I have this in datatemplate:
<toolkit:ToggleSwitch x:Name="LockSwitch" IsChecked="{Binding IsLock}" />
I could change binding to TwoWay but I change value in object and I want to call method in ViewModel with Module argument. So have can I change this binding? Should I someway call method in ViewModel from my object? Or should I somehow tell ViewModel that this object has changed this value? Or should I bind Checked and Unchecked event?
Regarding the Buttons:
You can access the "parent datacontext" with an elementName binding and set the command parameter:
<Button Command="{Binding DataContext.GoToXyCommand, ElementName=LayoutRoot}" CommandParameter="{Binding}" />
Regarding your second question:
First, I would check if a toggle-button is the right solution, if changing the value triggers a process with a possibly longer duration.
An example where WP does this is enabling/disabling Air-Plane Mode.
I would do it the same way:
Bind to the property via TwoWayBinding
When the property is changed, start the updating process, disable the toggle button and show a progress indicator.
EDIT: Here is and example from a ViewModel I recently used.
public bool IsUpdatingPushEnabled
{
get { return _isUpdatingPushEnabled; }
set { SetProperty(ref _isUpdatingPushEnabled, value); }
}
public bool IsPushEnabled
{
get { return _isPushEnabled; }
set
{
if (!IsUpdatingPushEnabled)
{
SetProperty(ref _isPushEnabled, value);
var t = SetPushAsync();
}
}
}
private async Task SetPushAsync()
{
IsUpdatingPushEnabled = true;
try
{
var result = await _settingService.EnablePushAsync(IsPushEnabled);
SetProperty(ref _isPushEnabled, result, "IsPushEnabled");
}
catch
{
//...
}
IsUpdatingPushEnabled = false;
}
Related
I have a question, I have a view and in that view I am having combobox:
<ComboBox ItemsSource="{Binding ProjectsBrowserAxmModules}" SelectedValuePath="AxmModuleId" DisplayMemberPath="AxmModuleName"
SelectedValue="{Binding SelectedAxmModule, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
controls:TextBoxHelper.Watermark="{Binding BrowserComboBoxWatermark}" Height="2" IsSynchronizedWithCurrentItem="True"
SelectionChanged="ComboBox_CurrentBrowserAxmChanged" >
And so event looks like:
private void ComboBox_CurrentBrowserAxmChanged(object sender, RoutedEventArgs e)
{
((CurrentsHistoryViewModel)DataContext).GetCurrentsModuleCommand.Execute(sender);
}
And every time i change view to different one and back to this view it looks like this event is registering anew so for example if i go to different view go back and go different view and back again this event will fire 4 times.
I tried
Loaded -= ComboBox_CurrentBrowserAxmChanged;
But no luck is there any way to unregistered those redundant events.
I believe, combobox SelectedValue property gets changed somehow and internally it calls your combobox selection changed event. have debugger point in setter of SelectedAxmModule property. hope you will find given property is hitting when you are switching screens.
My suggestion is , to remove selectionChanged event. and use event/delegate for same purpose.
Code Snipt:
public class AxmModule: NotifierModel
{
public static event MyEventHandler ValueChanges;
public delegate void MyEventHandler(string Value);
private string _selectedAxmModule ;
public string SelectedAxmModule
{
get
{
return _selectedAxmModule ;
}
set
{
_selectedAxmModule = value;
if (ValueChanged!= null)
{
ValueChanged(_selectedAxmModule );
RaisePropertyChanged("SelectedAxmModule ");
}
}
}
}
Register it in your view Code behind/ viewmodel
AxmModule.ValueChanged+= AxmModule_ValueChanged;
public void AxmModule_ValueChanged(string value)
{
// your code
}
Hope, this will resolve your issue.
In my WPF application I have a combo box. It binds data by MVVM design pattern. It works fine.
In the XAML
<ComboBox x:Name="comboBox1" ItemsSource="{Binding ValetType}" DisplayMemberPath="Name"
SelectedItem="{Binding SelectedItemValet, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True"/>
In the ViewModel
valetTypes = GetValueForValet();
public ObservableCollection<Valet> ValetType
{
get { return valetTypes; }
set { valetTypes = value; NotifyPropertyChanged("ValetType"); }
}
public Valet SelectedItemValet
{
get { return selectedItemValet;}
set { selectedItemValet = value; NotifyPropertyChanged("SelectedItemValet"); }
}
One of the problems I face is that when I do not change the combo box value it binds null, otherwise it binds. I set IsSynchronizedWithCurrentItem as true it tells a Selector should keep the SelectedItem synchronized with the current item. But would not work. How could I do this? I am new to WPF MVVM.
You´ve got one problem in your code:
public ValetServices SelectedItemValet
{
get { return selectedItemValet;}
set { selectedItemValet = value; NotifyPropertyChanged("SelectedItemValet"); }
}
ValetType is Type Valet, but SelectedItemValet is Type ValetServices. They should match. After changing that, to select the first item for example, do this:
valetTypes = GetValueForValet();
this.SelectedItemValet=ValetType.FirstOrDefault();
The easy solution would be to provide a default value to your SelectedItemValet.
In some method of your ViewModel, that is called when you ViewModel is initialized (typically the constructor) you can set SelectedItemValet to the value you want
I'm dealing with a popup which has a usercontrol as a child. This is done in a XAML file in UWP using Visual Studio 2015. In my usercontrol resides a button with which I would like to close the parenting popup. I can't seem to achieve this - I've tried several options and the closest was by using a relay command. It should be mentioned that the popup pops up as supposed to.
Here is the popup with usercontrol child. No namespace problems, since the popup is working.
Here is the button in the usercontrol.
What should my approach be? I'm using ViewModels (MVVM desing) for this, and both usercontrol have access to the same instance of a ViewModel class.
Cheers!
have a look at https://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.isopen(v=vs.110).aspx . You bind the Popup.IsOpen to a property in your view model ... the button sets the property to false.
<Popup IsOpen="{Binding ViewModel.IsOpen}" >
...
</Popup>
In the ViewModel you define a command that you can attach to AddCriteria button as such:
private ICommand _AddCriteriaCommand;
public ICommand AddCriteriaCommand
{
get
{
return _AddCriteriaCommand;
}
set
{
_AddCriteriaCommand = value;
}
}
And you initiate this command in the constructor or the getter of AddCriteriaCommand to a RelayCommand... as such
public VMConstructor(...)
{
_AddCriteriaCommand = new RelayCommand(execute => ClosePopup(), canExecute => true);
}
private void ClosePopup()
{
_IsOpen = false;
}
in your xaml assign the commadn to the AddCriteria button , something like
<local:AddCrteria Command="{Binding AddCriteriaCommand}" />
For the popup you simply create a property for it with change notification as such:
private bool _isOpen;
public bool IsOpen
{
get { return _isOpen; }
set
{
if (_isOpen == value) return;
_isOpen = value;
RaisePropertyChanged("IsOpen");
}
}
and finally bind the IsOpen property of the Popup control to that of the ViewModel as such:
IsOpen="{Binding IsOpen}"
And that should be it ... good luck.
See my CustomView window below
When I select project from the combobox, the client associated with that project should automatically displayed there.
In Combobox's selection changed event, I did so far
private string client
{
get
{
return ClientText.Text;
}
set
{
ClientText.Text = value;
}
}
public Harvest_Project projectClass
{
set
{
ProjectText.Text = value.ToString();
Harvest_Project proj = (Harvest_Project)ProjectText.Text; // shows error here. casting is not possible. What can I do here?
this.client = Globals._globalController.harvestManager.getClientEntriesThroughId(proj._client_id)._name;
PropertyChanged(this, new PropertyChangedEventArgs("client"));
}
}
public int project
{
get
{
return int.Parse(ProjectText.Text);
}
set
{
ProjectText.Text = value.ToString();
}
}
private void ProjectComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is ComboBoxItem)
{
ComboBoxItem item = (ComboBoxItem)sender;
}
}
In xaml I used binding like this,
<ComboBox x:Name="ProjectText" SelectionChanged="ProjectComboBoxChanged" ItemsSource="{Binding Path=projectList}" SelectedValuePath="_id" DisplayMemberPath="_name" SelectedItem="{Binding ProjectComboBoxChanged, Mode=OneWayToSource}" Background="Yellow" BorderThickness="0" Width="66"/>
In your event handler ProjectComboBoxChanged(object sender, SelectionChangedEventArgs e), sender is of type ComboBox and not ComboBoxItem, hence your if statement is always false.
e.AddedItems[0] will give you your desired ComboBoxItem. Make sure you check the count first.
Also, if all you want to do is to set the Text, you don't need to have the client property.
"client" is a property, it should be public.
Then PropertyChanged should be raised in the setter, so anytime you change client, the UI will know.
About the combo, SelectedItem should bind to a property, not a method. The property could be "client", but another property could be clearer.
In the setter of this property, you'll have the liberty to fix the new value of the "client" property.
And last, since you are using a binding for selectedItem, I see no reason to use the event selectionChanged. Use binding or event, not both.
Hope it helps ;)
I've started an MVVM project and now I'm stucking with correct DataBinding.
My project has:
A UserControl whit a ViewModel as DataContext like:
public partial class TestUserControl: UserControl
{
public TestUserControl()
{
this.DataContext = new TestUserControlViewModel();
}
}
ViewModel code is (BaseViewModel class contains PropertyChangedEventHandler):
public class TestUserControlViewModel : BaseViewModel
{
public KrankenkasseControlViewModel()
{}
public IEnumerable<DataItem> GetAllData
{
get
{
IGetTheData src= new DataRepository();
return src.GetData();
}
}
}
IGetTheData is the interface to DataContext:
public interface IGetTheData
{
IEnumerable<DataItem> GetData();
}
}
and finally the DataRepository code:
public class DataRepository : IGetTheData
{
private TestProjectDataContext dax = new TestProjectDataContext();
public IEnumerable<DataItem> GetData()
{
return (from d in this.dax.TestData
select new DataItem
{
ID = d.ID,
SomeOtherData = d.SomeOtherData
});
}
}
My UserControl has a few TextBoxes, but what's the best way to bind correctly?
Thanks for your help, regards.
EDIT: Binding the data against multiple textboxes
After reading your comment, I will elaborate my example for textboxes.
First important thing is that the ViewModel will model the things in the View, so that the View gets all information it needs in the structure it needs. That means, if you have multiple textboses in the View, you will need multiple string Properties in your ViewModel, one for each textbox.
In your XAML you could have something like
<TextBox Text="{Binding ID, Mode=TwoWay}" />
<TextBox Text="{Binding SomeOtherData, Mode=TwoWay}" />
and in your ViewModel
public class TestUserControlViewModel : BaseViewModel {
private string id;
private string someOtherData;
public TestUserControlViewModel() {
DataItem firstItem = new DataRepository().GetData().First();
this.ID = firstItem.ID;
this.SomeOtherData = firstItem.SomeOtherData;
}
public string ID {
get {
return this.id;
}
set {
if (this.id == value) return;
this.id = value;
this.OnPropertyChangedEvent("ID");
}
}
public string SomeOtherData {
get {
return this.someOtherData;
}
set {
if (this.someOtherData == value) return;
this.someOtherData = value;
this.OnPropertyChangedEvent("SomeOtherData");
}
}
}
Here I assume that in your BaseViewModel there is an OnPropertyChangedEvent method to fire the corresponding event. This tells the View that the property has changed and it must update itself.
Note the Mode=TwoWay in the XAML. This means, that it doesn't matter on which side the value changes, the other side will reflect the change immediately. So if the user changes a value in a TwoWay bound TextBox, then the corresponding ViewModel property will automatically change! And also vice versa: if you change the ViewModel property programmatically, the View will refresh.
If you want to show multiple textboxes for more than one data item, then you must introduce more Properties in the ViewModel and bind them accordingly. Maybe a ListBox with a flexible number of TextBoxes inside is a solution then, like #Haspemulator already answered.
Binding the data against a collection control
In the TestUserControl I guess you have a control (like a ListView) to show the list of loaded things. So bind that control against the list in the ViewModel with
<ListView ... ItemsSource="{Binding GetAllData}" ... />
First you must understand that Binding means not "read the data and then forget the ViewModel". Instead you bind the View to the ViewModel (and its Properties) as long as the View lasts. From this point of view, AllData is a much better name than GetAllData (thanks #Malcolm O'Hare).
Now in your code, every time the View reads the AllData property, a new DataRepository is created. Because of the Binding, that is not what you want, instead you want to have one instance of DataRepository for the whole lifetime of the View, which is used to read the initial data and can later be used to update the View, if the underlying database changes (maybe with an event).
To enable such a behavior you should change the type of the AllData property to an ObservableCollection, so that the View can automatically update the list if changes occur.
public class TestUserControlViewModel : BaseViewModel
private ObservableCollection<DataItem> allData;
public TestUserControlViewModel() {
IGetTheData src = new DataRepository();
this.allData = new ObservableCollection<DataItem>(src.GetData());
}
public ObservableCollection<DataItem> AllData {
get {
return this.allData;
}
}
public void AddDataItem(DataItem item) {
this.allData.Add(item);
}
}
Now if you call AddDataItem later, the ListView will update itself automatically.
Your Property Name is bad. You should call it AllData, not GetAllData.
Since you are returning a collection, you probably should be using some sort of list control (ListBox, ListView).
In that case you'd be doing
<ListBox ItemsSource="{Binding GetAllData}" />
Guten Abend. :) As it already mentioned, since you're returning the collection, it's better to use a ListBox. The comment about having ObservableCollection as a cache is also absolutely valid. I would add that if you need to have your data editable, you should use TextBox inside the ItemTemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text={Binding SomeOtherData,Mode=TwoWay} />
</DataTemplate>
</ListBox.ItemTemplate>
In this case if user edits the text in the box, data will be updated in your data object, so that it could be saved in the database later.