I feel bad posting this because I see a ton of similar posts, but after going through them all I can't quite diagnose my issue still. What I have is a WPF app designed with the MVVM pattern and using a RelayCommand() implementation for commands.
In my user control's XAML I set the data context here :
<UserControl.DataContext>
<viewModel:SidePanelViewModel />
</UserControl.DataContext>
Then further down in the XAML I have this snippet where I assign a button's command
<TextBlock FontWeight="Bold" Margin="0,0,0,10">Service List</TextBlock>
<ListBox MaxHeight="100"
ItemsSource="{Binding ServiceList}"
SelectedItem="{Binding ServiceToRemove}">
</ListBox>
<Button HorizontalAlignment="Left" Width="60" Margin="0,10"
Command="{Binding RemoveServiceCommand}">Remove</Button>
I am binding the button to the Command RemoveApplicationCommand which I define in the SidePanelViewModel here :
public ICommand RemoveServiceCommand
{
get { return new RelayCommand(RemoveService, CanRemoveService); }
}
private void RemoveService()
{
ServerList.Remove(ServiceToRemove);
}
private bool CanRemoveService()
{
return true;
}
The problem
If I debug, the getter for RemoveServiceCommand will be reached when the button starts up, but when I click the button the code doesn't reach it. I had a very similar implementation (or so I think) working before, so this is really puzzling me. How can I get the command to fire on click?
Command="{Binding RemoveApplicationCommand}"
Did you mean RemoveServiceCommand?
Turns out the debugger was going over RemoveService the entire time but I had not put a breakpoint there. I had a wrong name in my RemoveService implementation ServerList.Remove() should have been ServiceList.Remove(). I assumed the debugger would hit a breakpoint in the RemoveServiceCommand property's getter but it turns out it doesn't hit that when you click the button.
You're returning a new RelayCommand in your getting, but not saving / caching the instance. Save it in a member variable.
if (_cmd == null)
_cmd = new ....
return _cmd;
Try implementing like this
private ICommand finishCommand;
public ICommand FinishCommand
{
get
{
if (this.finishCommand == null)
{
this.finishCommand = new RelayCommand<object>(this.ExecuteFinishCommand, this.CanExecutFinishCommand);
}
return this.finishCommand;
}
}
private void ExecuteFinishCommand(object obj)
{
}
private bool CanExecutFinishCommand(object obj)
{
return true;
}
Related
I gave up on MVP pattern and gave MVVM and WPF a try. I managed so far to navigate the menu showing different UserControls by clicking different buttons. DataBinding and Commands are working fine for navigation however i ran into a problem when DataBinding a textbox.
The property always get the value null and i do not know why. I added a command to get the UserID from a textbox when clicking a button and to show a MessageBox containing the UserID just to make it simple before i try anything else.
The MessageBox shows but it is empty. I added a Task.Run in the UsersModel.cs so i can see if the string updates on propertychange but value is null.
I am using a class for ObservableObject to notify OnPropertyChanged. And a RelayCommand class that inherits from ICommand. Both seems to work as intended but only when clicking the buttons to show different UserControls and Close the application. Below is sample code from View, Model and ViewModel. What am i missing?
RegisterMenu.xaml
<TextBox x:Name="RegTextUserID" Margin="163,66,148,112"
Style="{StaticResource ResourceKey=UserBox}"
Text="{Binding UserID, UpdateSourceTrigger=PropertyChanged}">
<TextBox.DataContext>
<models:UsersModel/>
</TextBox.DataContext>
</TextBox>
<Button x:Name="Button_Register" Content="Register" HorizontalAlignment="Left"
Margin="183,129,0,0" VerticalAlignment="Top"
Command="{Binding Path=GetUserCommand}">
<Button.DataContext>
<viewmodels:RegisterViewModel/>
</Button.DataContext>
</Button>
UsersModel.cs
public class UsersModel : ObservableObject
{
// backing fields
private string _userID;
// Properties
public string UserID // UserID is null
{
get { return _userID; }
set
{
SetProperty(ref _userID, value);
}
}
// testing loop for propertychange trigger
public UsersModel()
{
Task.Run(() =>
{
while (true)
{
Debug.WriteLine($"UserID: {UserID}");
Thread.Sleep(1000);
}
});
}
}
RegisterViewModel.cs
public class RegisterViewModel : ObservableObject
{
public RelayCommand GetUserCommand { get; set; }
public RegisterViewModel()
{
GetUserCommand = new RelayCommand(o =>
{
// Command is responding to button click.
});
}
}
EDIT 1
Updated the code in UsersModel.cs were i changed ObservableObject.cs to use [CallerMemberName] and SetProperty. I also removed the GetUserCommand code from RegisterViewModel.cs so i can only test the property in UsersModel.cs.
From what i understand is that the textBox binding is not working. I do not get any value even after using SetProperty method.
EDIT 2
To clarify, i used the following in XAML earlier (One DataContext) at the start. But i still had the same problem. The UsersModel property is not getting the text i type in the textbox.
<UserControl.DataContext>
<models: UserModel/>
</UserControl.DataContext>
I created a sample project with one textbox to try out setting a specific DataContext for the textbox like the code below. It is working in the sample. The output prints the text i write in the textbox. Why is it not working in the UsersModel.cs above?
MainWindow.xaml
<TextBox HorizontalAlignment="Left" Margin="249,60,0,0"
Text=" {Binding SomeText, UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"
TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.DataContext>
<local:TestModel/>
</TextBox.DataContext>
</TextBox>
TestModel.cs (this works, i can see the loop output everytime i change the text in the textbox).
class TestModel
{
private string _someText;
public string SomeText
{
get { return _someText; }
set { _someText = value; }
}
public TestModel()
{
Task.Run(() =>
{
while (true)
{
Debug.WriteLine(SomeText);
Thread.Sleep(1000);
}
});
}
}
I was looking for the error in the wrong place. I managed to locate the error to the Style. I've tested to remove the style and the binding worked fine. I have to adjust the code of the Themes i am using. Thanks for all the comments.
I have an application list which is bound to a ListView control.
private List<_Application> _applicationList;
public List<_Application> applicationList
{
get { return _applicationList; }
set
{
_applicationList = value;
OnPropertyChanged();
}
}
The ListView ItemTemplate is set as a button.
<ListView ItemsSource="{Binding applicationList}"
BorderThickness="5"
Style="{DynamicResource ListViewStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Command="{Binding RunCommand}"
Style="{StaticResource ApplicationButtonStyle}"
Content="{Binding name}"
Background="{Binding colorRGB}" >
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When I click on the button I want an application to be executed. My Model _Application contains an ActionCommand that runs the process.
public class _Application
{
public ActionCommand RunCommand
{
get { return new ActionCommand(action => Run()); }
}
private void Run()
{
Process p = new Process();
p.StartInfo.FileName = path;
try
{
p.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public _Application()
{
}
}
I am not sure, is it correct keeping the ActionCommand in the Model?
How could I correctly implement this in the MVVM pattern?
Where should the ActionCommand be placed and how to bind it to the ListView of Buttons so the correct _Application will be run?
I think the best way is pass the model (_Application) like a parameter to the command.
RunCommand = new RelayCommand(param => this.OnRun(param));
Command action
private void OnRun(_Application app)
{
//Any action with your model
}
Xaml
Command="{Binding DataContext.RunCommand, ElementName=PageRootKey}"
CommandParameter="{Binding Mode=OneWay}">
First of all you should use ICommand and not ActionCommand from one simple reason, if in the future you will want to replace the ActionCommand in something better that implements ICommand you won`t need to replace so many places in the code.
public ICommand RunCommand
{
get
{ return new ActionCommand(Run); }
}
The correct _Application will run as each item in the list view is connected to single _Application item in the collection.
Note: in above code I wrote ...ActionCommand(Run); since ActionCommand accepts an Action parameter you can write the code shortly and more readable like this.
I assume of course in the complete code _Application has properties of name and colorRgb.
As a matter of fact, if you wish to use correct MVVM pattern then colorRgb should not be in the viewmodel or the model. It is a view term. You should use a converter (read IValueConverter) to set a different color for each button (although it is not UX friendly).
And one last thing, property like name should be Name (capital N) as property names in C# should always begin with capital letters.
I am quiet new to programming and am currently learning C# and the MVVMC pattern (which is I think basically the same as MVVM pattern).
I need to code a database tool for ChiliPlants for university. There you should be able to edit an existing item from an ObservableCollection.
This ObservableCollection I displayed in a DataGrid, see this:DataGrid
Below the DataGrid there are three buttons: Add, Edit and Delete.
I was able to programm the AddButton, aswell as the DeleteButton.
Unfortunately I don't know how to programm the EditButton.
It should open a new window, where the SelectedItem should be opened like this:EditWindow
Until now my EditButton does the same thing as my AddButton.
See my code here:
View:
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="Add" Margin="5,5,0,5" Width="100" Command="{Binding AddCommand}" />
<Button Content="Edit" Margin="5,5,0,5" Width="100" Command="{Binding EditCommand}" />
<Button Content="Delete" Margin="5,5,540,5" Width="100" Command="{Binding DeleteCommand}" />
<Button Content="Sichern" Margin="5,5,5,5" Width="100" Command="{Binding SaveCommand}" />
</StackPanel>
ViewModel:
private ICommand _editCommand;
public ICommand EditCommand
{
get { return _editCommand; }
set { _editCommand = value; }
}
Controller:
public void SDInitialize()
{
var view = new WindowStammdatenverwaltung();
mViewModel = new WindowStammdatenverwaltungViewModel
{
EditCommand = new RelayCommand(EditCommandExecute, EditCommandCanExecute)
};
view.DataContext = mViewModel;
view.ShowDialog();
}
private void EditCommandExecute(object obj)
{
var editedObject = new WindowEditController().EditChiliModel();
if (editedObject != null)
{
mViewModel.Stock.Add(mViewModel.SelectedChili);
}
}
private bool EditCommandCanExecute(object obj)
{
return mViewModel.SelectedChili != null;
}
The problem is with the EditCommandExecute. Currently I have just put the Code for an AddCommandExecute in there. I unfortunately don't know how to code such an EditCommandExecute.
My WindowEditController looks like this:
public class WindowEditController
{
WindowEdit mView;
public ChiliModel EditChiliModel()
{
mView = new WindowEdit();
WindowEditViewModel mViewModel = new WindowEditViewModel
{
ChiliModel = new ChiliModel(),
OkCommand = new RelayCommand(ExecuteOkCommand),
CancelCommand = new RelayCommand(ExecuteCancelCommand),
};
mView.DataContext = mViewModel;
if (mView.ShowDialog() == true)
{
return mViewModel.ChiliModel;
}
else
{
return null;
}
}
private void ExecuteOkCommand(object obj)
{
mView.DialogResult = true;
mView.Close();
}
private void ExecuteCancelCommand(object obj)
{
mView.DialogResult = false;
mView.Close();
}
I know, that I could let the user edit the SelectedItem inside the DataGrid, but this is not allowed in my task...
Could I maybe use the same window as for my AddCommand? Basically they should look the same, the EditWindow should just already contain the information of the SelectedItem.
I looked up almost every entry similar to this topic, but I did not find a simple solution. Or a solution which I was able to understand with my bad coding skills :( ...
I would be very happy if you guys could help me. Please keep it simple for this newbie :)
What I already tried:
I tried to add a CommandParameter to my Button looking like this: CommandParameter="{Binding SelectedItem, ElementName=StockDataGrid}"
But this still didn't open the window containing the data of the SelectedItem. It just opened a completely new Window for a new Item.
Use CommandParameter just after the Command property, and bind it to the SelectedItem of the DataGrid.
For example, suppose that you DataGrid has the attribute Name=MyDataGrid.
The Button becomes:
<Button Content="Edit"
Margin="5,5,0,5"
Width="100"
Command="{Binding EditCommand}"
CommandParameter="{Binding SelectedItem, ElementName=MyDataGrid}"/>
When EditCommandExecute(object obj) is executed, obj is actually the current SelectedItem that you want.
I have an icon on my Xamarin.Forms app, which when it is clicked, I would like to change it to an activity indicator. It looks as though I should use a Trigger, Event Triggers look good, but as my image is declared in XAML, I am not sure quite how it would fit together?
At the moment, I have this in XAML at the bottom of a stacklayout:
<Button x:Name="NewDeviceButton"
Image="glyphish_31_circle_x.png"
HorizontalOptions="End"
VerticalOptions="EndAndExpand" />
When it is clicked, I would like to show this, for a set amount of time, then trigger some C# functionality:
<ActivityIndicator Color="Black" IsRunning="true" />
I am not sure whether it would be best to configure it all in XAML with a trigger, or if i can just have a placeholder type item in XAML and then have all the definitions in C#?
There are a few ways of doing this.
You could simply give each of them an x:Name and then turn on/off IsRunning and IsVisible if you want to hide it.
I assume though that you have some data binding going on. Since IsRunning is a bool you could simply bind it to a boolean in your code behind. For instance In my ViewModel I have an IsBusy property and implement INotifyPropertyChanged:
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
}
private bool busy = false;
public bool IsBusy
{
get { return busy; }
set
{
if (busy == value)
return;
busy = value;
OnPropertyChanged("IsBusy");
}
}
public async Task GetMonkeysAsync()
{
if (IsBusy)
return;
try
{
IsBusy = true;
//do stuff here that is going to take a while
}
finally
{
IsBusy = false;
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
var changed = PropertyChanged;
if (changed == null)
return;
changed(this, new PropertyChangedEventArgs(name));
}
#endregion
}
Then in the XAML you can bind to IsBusy:
<ActivityIndicator IsRunning="{Binding IsBusy}"
Color="Blue"/>
I think that should handle it. If you need a timer you could use the same binding that I have here and use Xamarin.Forms built in timer class.
James' answer is correct, however, I prefer to use the page's own IsBusy property, for two reasons:
I'm lazy and I don't want to set-up INotifyPropertyChanged implementation if I don't have to
XF is suppossed to use that property to display a notification by default. This doesn't appear to be working, but if it ever does, this approach will already have us covered to take advantage of it
The only difference with James' answer (besides deleting the INPC implementation) is that you need to give the page a name (x:Name="myPage") and then declare the binding using a reference to it using {Binding Source={x:Reference myPage}, Path=IsBusy} for the ActivityIndicator's IsVisible and IsRunning values.
i.e.:
MainPage.xaml:
<ContentPage ... x:Name="myPage">
...
<ActivityIndicator IsVisible="{Binding Source={x:Reference myPage}, Path=IsBusy}" IsRunning="{Binding Source={x:Reference myPage}, Path=IsBusy}" />
...
</ContentPage>
MainPage.xaml.cs:
...
async void OnDoSomethingLong(...)
{
if (!this.IsBusy)
{
try
{
this.IsBusy = true;
//await long operation here, i.e.:
await Task.Run(() => {/*your long code*/});
}
finally
{
this.IsBusy = false;
}
}
}
...
I'm using two views which refers same view model. Both of my views contain a text box that binds to a value in the view model. My problem is that, if I change the value of textbox in one GUI, its not reflecting in another. What should I do to achieve this?
This is my view model
public class ProductViewModel:INotifyPropertyChanged
{
private int machineheight;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public int MachineHeight
{
get
{
return this.machineheight;
}
set
{
this.machineheight = value;
RaisePropertyChanged("MachineHeight");
}
}
public ProductViewModel()
{
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
SecondWindow w = new SecondWindow();
w.Show();
}
#endregion
}
}
}
The second window is another GUI. Once I click update button, second window opened. But the value that I have changed in first UI is not updated in the new window.
My Xaml is similar for both UI..
<Window x:Class="WPFDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFDemo"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ProductViewModel/>
</Window.DataContext>
<Grid Height="307" Width="480" Initialized="Grid_Initialized">
<Button Content="Update" Height="32" HorizontalAlignment="Left" Margin="165,158,0,0" Name="button1" VerticalAlignment="Top" Width="114" Command="{Binding Path=UpdateCommand}"/>
<TextBox Height="42" HorizontalAlignment="Left" Margin="125,82,0,0" Name="textBox1" VerticalAlignment="Top" Width="169" Text= "{Binding Path= MachineHeight, Mode=TwoWay}" />
</Grid>
</Window>
I actually don't know what is the problem.. thanks
<Window.DataContext>
<local:ProductViewModel/>
</Window.DataContext>
hi, if you put this in your 2 views, then each one has its own viewmodel. so you will never see any changes. you have to set the datacontext from your first view to your second view. Btw for your ICommand implementation look at some mvvm frameworks for easier implementations, eg RelayCommand, DelegateCommand.
For your actual implementation you can add the following to your xaml and ViewModel(CommandParameter) then it works.
<Button Content="Update" Height="32" HorizontalAlignment="Left" Margin="165,158,0,0" Name="button1" VerticalAlignment="Top" Width="114" Command="{Binding Path=UpdateCommand}"
CommandParameter="{Binding .}"/>
public void Execute(object parameter)
{
SecondWindow w = new SecondWindow();
w.DataContext = parameter;
w.Show();
}
There are a hundred things that can go wrong in this scenario, and one of my long-standing gripes with XAML-based databinding is that the MS tools give you precious little help figuring out which of those hundred things it is. This is especially the case if you're new to databinding, but even folks who've been doing it for years can spend obnoxious hours tracking down databinding issues.
Some things to check:
(1) Confirm that your databindings are two-way.
(2) Look in your debug output window to see if there are any error messages there.
(3) Set an IValueConverter in your databinding, and set a breakpoint in the converter to see what data is being passed where and when.
(4) Confirm that the data in the ViewModel is actually being updated.
(5) Confirm that the ViewModel implements INotifyPropertyChanged, and that the PropertyChanged event is firing.
(6) Post your actual code here so folks can look at it.
And so forth.
Hope this helps.
It must work if the ViewModel implements INotifyPropertyChanged.