Ok I'll make this very simple! Here are viewmodels :
public class ObjectsModel
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _objectName;
public string ObjectName
{
get
{
return _objectName;
}
set
{
if (value != _objectName)
{
_objectName = value;
PropertyChanged(this, new PropertyChangedEventArgs("ObjectName"));
}
}
}
public IEnumerable<Object> Objects {get;set;}
public ICommand AddCommand { get; private set; }
public ICommand SaveChangesCommand { get; private set; }
myDomainContext context = new myDomainContext();
public ObjectsModel()
{
objects = context.Objects;
context.Load(context.GetObjectsQuery());
}
}
public class InventoryModel
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public IEnumerable<Inventory> Inventories {get;set;}
public ICommand AddCommand { get; private set; }
public ICommand SaveChangesCommand { get; private set; }
myDomainContext context = new myDomainContext();
public ObjectsModel()
{
objects = context.Objects;
context.Load(context.GetObjectsQuery());
}
}
So what I'm trying to do is in my second form where I want to add an inventory for an object, I have to select the object in a combobox. The question is, how do I fill my combobox? Create another instance of the "ObjectsModel" in the InventoryModel? or use another "context" where I would query the other table? Or is there an easier Xaml approach? If I'm not clear, tell me I'll put more examples/code.
tx a lot!
You want to bind the contents of the combobox to a list of items provided by your ViewModel and bind the selected item to another property on the same ViewModel.
Please get into the habit of naming actual view models to end in "ViewModel", rather than "Model", so they do not clash with your other "real" models. It actually looks like you are binding directly to your business models instead of ViewModels (which is not good).
Related
I have an application that look like this
The whole window is defined in the MainWindow.xaml, the green part is the content control
<ContentControl Grid.Row="1"
Grid.Column="1"
Margin="5"
Content="{Binding CurrentView}"/>
The MainViewModel looks like this:
public RelayCommand HomeViewCommand { get; set; }
public RelayCommand DetailsViewCommand { get; set; }
public HomeViewModel HomeVm { get; set; }
public DetailsViewModel DetailsVm { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
HomeVm = new HomeViewModel();
DetailsVm = new DetailsViewModel();
CurrentView = HomeVm;
HomeViewCommand = new RelayCommand(o =>
{
CurrentView = HomeVm;
});
}
Current and default content of the MainView is the HomeView, I already implemented the event trigger on pressing the item in the list in the HomeView. I want to know, what should I write in the HomeView method (which is triggering on the click on the item) in order to change the MainView content part to another View (DetailsView in my case). Code in my HomeViewModel:
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
//something here to change the currentView of the MainViewModel
}
}
Got an answer from the guy from discord server.https://discord.gg/AvnpSMDY
You could pass the MainViewModel into the constructor of another view model and assign it to a private readonly field like _mainViewModel. This way you can change the current view either by changing the CurrentView property:
_mainViewModel.CurrentView = _mainViewModel.DetailsVm;
or executing commands:
_mainViewModel.DetailsViewCommand.Execute(null);
I am trying to binding a number to a Enum located in the view model. I haven't been able to pass the value from the view to the viewmodel through the converter. Is this even possible? I haven't seen anything online that solves this and none of my attemps have worked either.
Viewmodel
public enum TimerOptions
{
FifteenMinutes,
OneHour,
Tomorrow
}
private ICommand _timerCommand;
public ICommand TimerCommand => _timerCommand ??
(_timerCommand = new RelayCommand<TimerOptions>(StartTimer));
private async void StartTimer(TimerOptions option){ .... }
View
<Button Command="{Binding TimerCommand}"
Tag="0"
CommandParameter="{Binding Path=Tag, Converter={StaticResource BidirectionalEnumConverter}}">15 minutes</Button>
In the above example, when the user clicks the button, I want my function to get the first enum value of FifteenMinutes as the parameter. I've tried this by adding a Tag (as seen), adding an x:Name to the Button and also playing around with Source and Path in the binding. Nothing has worked.
Is this possible? I've been trying to solve this for hours, I haven't found anything online.
I want my function to get the first enum value of FifteenMinutes as the parameter.
Why not just do the converter operation in the VM from the info passed in on a string in the command parameter?
Example
<Button Command="{Binding TimerCommand}"
Tag="1"
CommandParameter=1
Then have a ICommand based on that such as
public ICommand TimerCommand =>
new OperationCommand(async (oTag) => await StartTime(oTag).ConfigureAwait(true));
private bool IsOperation { get; set; }
private async void StartTimer(object oTag)
{
int tagId = (int)oTag;
//.... do the convert operations here...
...
// Access VM properties here
IsOperation = true;
await ....;
}
Here is the commanding structure I use instead of Relay.
public class OperationCommand : ICommand
{
#region Variables
private Func<object, bool> CanExecuteHandler { get; set; }
private Action<object> ExecuteActionHandler { get; set; }
public bool InSeparateThread { get; set; }
#endregion
#region Properties
#endregion
#region Construction/Initialization
public OperationCommand(Action<object> executeAction, bool inSeparateThread = false)
{
InSeparateThread = inSeparateThread;
ExecuteActionHandler = executeAction
?? throw new ArgumentNullException(nameof(executeAction));
}
public OperationCommand(Action<object> executeAction,
Func<object, bool> canExecute) : this(executeAction)
{
CanExecuteHandler = canExecute;
}
// Here to adhere to ICommand, change to below if needed
//public event EventHandler CanExecuteChanged;
event EventHandler ICommand.CanExecuteChanged
{
add {}
remove {}
}
#endregion
#region Methods
public bool CanExecute(object parameter) => CanExecuteHandler?.Invoke(parameter) ?? true;
// public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, new EventArgs());
public void Execute(object parameter)
{
ExecuteActionHandler?.Invoke(parameter);
}
#endregion
}
}
To simplify situation. I have MainWindow with two user controls, all of them have corresponding Viewmodels. Everything works fine, properties bind and so on, beside one functionality.
I want to refresh data on second user control after event happened in the first one. Unfortunetly in this scenario, PropertyChanged event (derived from INotifyPropertyChanged, defined in ViewModelBase) is null.
However, if I raise an event from second user control, property on view gets updated as expected!
public class MainWindowViewModel : ViewModelBase
{
public FirstUserControl FirstUserControl {get; set;}
public SecondUserControl SecondUserControl {get; set;}
public MainWindowViewModel ()
{
FirstUserControl =new FirstUserControl();
FirstUserControl.RaiseClicked+=OnRaiseClicked;
SecondUserControl = new SecondUserControl();
SecondUserControl .RaiseClicked+=OnRaiseClicked;
}
private void OnRaiseClicked(object sender, EventArgs e)
{
SecondUserControl.RefreshView();
}
}
public class FirstUserControl : ViewModelBase
{
public ICommand Raise { get; private set; }
public EventHandler RaiseClicked {get;set;}
public FirstUserControl ()
{
Raise = new RelayCommand( p=> RaiseClicked(this, null));
}
}
public class SecondUserControl: ViewModelBase
{
public ICommand Raise { get; private set; }
public EventHandler RaiseClicked {get;set;}
public string Title
{
get
{
return MyLogic.GetCurrentTitle(); // debuggers enter here only while event on second user control raised
}
}
public void RefreshView()
{
OnPropertyChanged("Title"); // debugger enter here in cases
}
}
I suppose there is something with threads going on, but I'm not that familiar with WPF to work out it by my own. Can someone help how to quickly and easy make event from first UC refresh data on the second?
In the examples for coding with the MVVM pattern and WPF binding, they use INotifyPropertyChanged when it's a single value and ObservableCollection when it is a list of values.
I've also read, you cannot use static variables with INotifyPropertyChanged, but you can with ObservableCollection. I'd like to bind to a static variable.
The easiest (to me at least) workaround, is to use an ObservableCollection and always just use and bind to index 0. Is this appropriate to do? Is there any benefit to using INotifyPropertyChanged instead of an ObservableCollection?
For example:
This seems to be the simplest workaround:
public static ObservableCollection<int> MyValues { get; set; }
<TextBox Text="{Binding MyValue[0]}">
For wanting to do this:
private static int _myValue;
public static int MyValue //does not work
{
get { return _myValue; }
set { _myValue = value; OnPropertyChange(); }
}
<TextBox Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}">
I don't think your analogy is fair(Comparing ObservableCollection with directly with Primitive Type property). Compare ObservableCollection to below class
public class MyClass : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set { text = value;
RaiseChange("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChange(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
}
Now both below will behave same:
public static MyClass Text { get; set; }
public static ObservableCollection<string> Text { get; set; }
If you perform Text.Text = "Hello" for MyClass or Text[0] = "Hello" for ObservableCollection, both will reflected in same way.
Now if you have to use a static property for binding then instead of ObservableCollection I'll advice you to write your new class
cause ObservableCollection has many internal implementations which
probably is of no use to you any actually consuming memory &
perforamnce.
This link may help, it shows the difference between List, ObservableCollection and INotifyPropertyChanged.
Hope this help
I would like that my UIElement control acts like a LinkButton in ASP.Net when clicked, and sends an CommandArgument on MouseLeftButtonUp. I suppose I need to make a custom event for this, so I created a OnCommand event like this:
public delegate void OnCommand(object sender, CommandEventArgs e);
public class CommandEventArgs : EventArgs
{
public string CommandArgument { get; set; }
}
How can I add this event to MouseLeftButtonUp on my UIElement, and also pass the CommandArgument? Or is there another way to accomplish the effect of a LinkButton?
I didnt figure out a solution with custom events, so I made a UserControl instead, with a CommandArgument and CommandName properties.
public partial class LinkButton : UserControl
{
public string CommandArgument { get; set; }
public string CommandName { get; set; }
public LinkButton()
{
InitializeComponent();
CommandArgument = String.Empty;
CommandName = String.Empty;
}
public void AddElemnent(UIElement obj)
{
LayoutRoot.Children.Add(obj);
}
}