I having the following xaml and code of the view model,currently I bind the screen list view to the view model .
the user control have text box and button and when the user click on the button (Go) I want to get the data from the view ,how should I do that?
currently I always get the data when I run the window but
I want the list to be empty when I open the page and when I click on
the GO button the list will be filled
<Grid Width="877" Height="632"
DataContext="{Binding Source={StaticResource ConfigServiceModelViewDataSource}}" >
<Grid.ColumnDefinitions>
<UserControl.Resources>
<ViewModel:ConfigServiceModelView x:Key="ConfigServiceModelViewDataSource" />
<DataTemplate x:Key="CollectionTemplate">
</DataTemplate>
</UserControl.Resources>
<ListView Grid.Column="2" HorizontalAlignment="Center" Height="230"
Margin="5,20,0,0" Grid.Row="2" VerticalAlignment="Top" Width="330"
ItemsSource="{Binding GetCollection}" }" >
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" />
in the ModelView Im getting the data from the model like
internal class ConfigModelView {
private ConfigServiceModel _configServiceModel = new ConfigServiceModel();
public List<string> GetServiceCollection {
get {
return _configServiceModel.CollectList;
}
}
}
Try this
ViewModel
public class ConfigModelView
{
public ConfigModelView()
{
GetServiceCollection = new ObservableCollection<string>();
}
bool isDataLoaded = false;
MyCommand goCommand;
public ICommand GoCommand
{
get { return goCommand ?? (goCommand = new MyCommand(() => OnGoCommand(), () => !isDataLoaded)); }
}
public ObservableCollection<string> GetServiceCollection { get; set; }
void OnGoCommand()
{
GetServiceCollection.Clear();
foreach (var item in _configServiceModel.CollectList)
{
GetServiceCollection.Add(item);
}
isDataLoaded = true;
goCommand.RaiseCanExecuteChanged();
}
}
Custom Command .You can use RelayCommand
public class MyCommand : ICommand
{
private Action _action;
private Func<bool> _canExecute;
public MyCommand(Action action, Func<bool> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
public void RaiseCanExecuteChanged()
{
if(CanExecuteChanged!=null)
CanExecuteChanged(this,new EventArgs());
}
}
xaml
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" Command="{Binding GoCommand}"/>
I hope this will help.
Related
I have a simple app, that should add SelectedItem from ComboBox to ListBox.
I have Model:Player
public class Player
{
public int ID { get; set; }
public string Name { get; set; }
private bool _isSelected = false;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; }
}
}
And ObservableCollection property in my ViewModel (Players)
public class ViewModel
{
public ObservableCollection<Player> Players { get; set; }
public ObservableCollection<Player> PlayersInTournament { get; set; } = new ObservableCollection<Player>();
public ICommand AddPlayerCommand { get; set; }
public ViewModel()
{
DataAccess access = new DataAccess();
Players = new ObservableCollection<Player>(access.GetPlayers());//GetPlayers from DataBase
AddPlayerCommand = new RelayCommand(AddPlayer, CanAddPlayer);
}
private void AddPlayer()
{
//Something like PlayersInTournamen.Add(SelectedPlayer);
}
private bool CanAddPlayer()
{
bool canAdd = false;
foreach(Player player in Players)
{
if (player.IsSelected == true)
canAdd = true;
}
return canAdd;
}
}
Property(ItemSource) of my ComboBox is bound to the Players collection. When the application is Loaded my ComboBox is filled with objects from DataBase and when I select one of them it is displayed in my ReadOnly TextBox. I achieved this by binding the Text property to the ItemSelected.Name property of ComboBox. There is an Add button in the app that add selected player to the tournament(ListBox)(the app is about tournament). ListBox's ItemSource is PlayersInTournament collection(see in ViewModel).
XAML(DataContext of Window is set to ViewModel instance after InitializeComponents()):
<Window x:Class="ComboBoxDemoSQL.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:ComboBoxDemoSQL"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<StackPanel HorizontalAlignment="Center"
Orientation="Horizontal" Margin="0 40 0 10">
<TextBox x:Name="HoldPlayerTextBox"
Width="100"
Text="{Binding ElementName=PlayersComboBox, Path=SelectedItem.Name}"
IsReadOnly="True">
</TextBox>
<ComboBox Name="PlayersComboBox"
VerticalAlignment="Top"
Margin="10 0 0 0"
HorizontalAlignment="Center" Width="100"
ItemsSource="{Binding Players}"
DisplayMemberPath="Name"
Text="Select player"
IsEditable="True"
IsReadOnly="True"/>
</StackPanel>
<Button Content="Add" Margin="120 0 120 0"
Command="{Binding AddPlayerCommand}"/>
<ListBox Margin="10" ItemsSource="{Binding PlayersInTournament}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding ID}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
Photo to understand better:
So basically there are 2 problems:
I don't know how to add to the PlayersInTournament collection a
player that is selected in ComboBox because I can't get the name
of that Player from TexBox(because its' Text property is bound to
another Property)
I don't know how to disable Add Button(CanAddPlayer method) when
there is no Player selected, I tried by adding IsSelected(see
Player model) property, but for it to work I have to bind to any
property in View that would change it, but I don't know which
property can be used for this thing.
ICommand implementation:
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private Action methodToExecute;
private Func<bool> canExecuteEvaluator;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
this.methodToExecute = methodToExecute;
this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
: this(methodToExecute, null)
{
}
public bool CanExecute(object parameter)
{
if (this.canExecuteEvaluator == null)
{
return true;
}
else
{
bool result = this.canExecuteEvaluator.Invoke();
return result;
}
}
public void Execute(object parameter)
{
this.methodToExecute.Invoke();
}
}
May I suggest the following.
You can override the ToString() method of your Player class to ease display in your ComboBox e.g.:
public class Player
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
By default ComboBox binding will call the ToString() method of whatever property it is bound to.
If you bind ComboBox.SelectedItem to a new Player property in the ViewModel, you can clear the selected player text in the ComboBox from code in the ViewModel.
If you add a CommandParameter to your Button binding, you can pass the selected player instance to the command, but this isn't strictly needed once you have a bound property in your ViewModel.
Thus your XAML becomes something like this:
<ComboBox x:Name="ComboBox"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Width="100"
Text="Select player"
SelectedItem="{Binding SelectedPlayer}"
ItemsSource="{Binding Players}"/>
<Button x:Name="ButtonAddPlayer"
Content="Add"
Command="{Binding AddPlayerCommand}"
CommandParameter="{Binding SelectedPlayer}"
HorizontalAlignment="Left"
Margin="62,176,0,0"
VerticalAlignment="Top"
Width="75"/>
And your ViewModel contains:
public ObservableCollection<Player> PlayersInTournament { get; set; }
public ObservableCollection<Player> Players { get; set; }
private Player _selectedPlayer;
public Player SelectedPlayer
{
get => _selectedPlayer;
set => SetField(ref _selectedPlayer, value);
}
public ICommand AddPlayerCommand { get; set; }
private bool CanAddPlayer(object obj)
{
return SelectedPlayer != null;
}
private void AddPlayer(object param)
{
if (param is Player player)
{
PlayersInTournament.Add(player);
Players.Remove(player);
SelectedPlayer = null;
};
}
Note that in the above code, as a player is added to the tournament list it is removed from the available players list preventing reselection of the same player.
Setting the SelectedPlayer property to null not only clears the ComboBox.SelectedItem display but also disables the Add button.
Also if you are likely to have several properties that you implement a helper function to handle your INotifyPropertyChanged events.
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
You can use CommandParameter in xaml:
<Button Content="Add" Margin="120 0 120 0"
Command="{Binding AddPlayerCommand}"
CommandParameter="{Binding Path=SelectedItem, Source=PlayersComboBox}"/>
in your ViewModel:
private ICommand _addPlayerCommand;
public ICommand AddPlayerCommand
{
get
{
if (_addPlayerCommand== null)
{
_addPlayerCommand= new RelayCommand(param => OnAddPlayerClicked(param));
}
return _addPlayerCommand;
}
}
private void AddPlayer(object param)
{
Player selectedPlayer = (player)param;
PlayersInTournamen.Add(SelectedPlayer);
}
RelayCommand:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
}
I hope this helps.
I have a listbox with a couple of buttons underneath.
<ListBox ItemsSource="{Binding SongList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=.}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Horizontal">
<Button Content="Add" Command="{Binding addSongCommand}" />
<Button Content="Remove"/>
</StackPanel>
SongList1
When I click the add button I want the add and remove buttons to be replaced by a textbox and submit button.
SongList2
Then when I click submit i want it to add the entered string into the collection (SongList) and bring back the add and remove buttons.
SongList3
How would the hiding and showing of controls be done with MVVM? Assuming that I have access to this views viewmodel in the addSongCommand.Execute() method, what logic would I put there?
public class AddSongCommand : CommandBase
{
private ViewModelBase _vm;
public AddSongCommand(ViewModelBase vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
// what goes here?
}
}
<ListBox ItemsSource="{Binding SongList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=.}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Horizontal" Visibility="{Binding IsAdding, Converter={booltovisflipconverter}}">
<Button Content="Add" Command="{Binding AddSongCommand}" />
<Button Content="Remove"/>
</StackPanel>
<StackPanel Orientation="Vertical" Visibility="{Binding IsAdding, Converter={booltovisconverter}}">
<TextBox Text="{Binding Song}"/>
<Button Content="Submit" Command="{Binding SubmitSongCommand}" />
</StackPanel>
Note: You will need to write the converter to flip the visibility, or use a datatrigger.
public class ViewModel : ViewModelBase
{
public ObservableCollection<string> SongList {get;set;} = new ObservableCollection<string>();
public bool IsAdding
{
get { ... }
set { notifychanged }
}
public string Song
{
get { ... }
set { notifychanged }
}
// called from add song command
public void EnableAdding()
{
IsAdding = true;
}
// called from submit command
public void SubmitSong()
{
SongList.Add(Song);
IsAdding = false;
}
}
public class SubmitSongCommand : CommandBase
{
private ViewModel _vm;
public SubmitSongCommand(ViewModel vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
_vm.SubmitSong();
}
}
public class AddSongCommand : CommandBase
{
private ViewModel _vm;
public AddSongCommand(ViewModel vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
_vm.EnableAdding();
}
}
The above uses specific types for each command. So many commands = many types.
You could implement a basic command using delegates instead.
public class SimpleCommand : ICommand
{
public Action<object> ExecuteAction {get;set;}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ExecuteAction?.Invoke(parameter);
}
}
public class ViewModel
{
public ViewModel()
{
AddSongCommand = new SimpleCommand()
{
ExecuteAction = (x) => { AddSong(); }
};
SubmitSongCommand = new SimpleCommand()
{
ExecuteAction = (x) => { SubmitSong(); }
};
}
public ICommand AddSongCommand { get; }
public ICommand SubmitSongCommand { get; }
public void AddSong()
{
// add song to list
}
public void SubmitSong()
{
// submit song
}
}
So I have this project where I have two buttons and a ListView. The ListView is separated into it's own UserControl with it's own ViewModel which contains a ObservableCollection.
I'm using a ContentPresenter to display that control because I will be using different views.
Currently, when I'm clicking the Log button, it does in fact add the string to the collection, but the view doesn't update. And it keeps adding more and more everytime I click on it. (I've put a breakpoint inside private void AddItemOne() to inspect it to prove that it adds items.)
Question
Why doesn't my view update when I click the "Log" button even though it's adding items.
It does add the first item if I hardcode it like this.
public LogViewModel()
{
Logs = new ObservableCollection<string>();
Logs.Add("Test");
}
MainWindow.xaml
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" Height="25" Content="Log"
Command="{Binding AddItemOneCommand}"/>
<Button Grid.Row="0" Grid.Column="1" Height="25" Content="Other"
Command="{Binding AddItemTwoCommand}"/>
<UserControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
MainViewModel.cs
class MainViewModel
{
public RelayCommand AddItemOneCommand { get; set; }
public RelayCommand AddItemTwoCommand { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
}
}
/* ViewModels */
public LogViewModel LogViewModel { get; set; }
public MainViewModel()
{
AddItemOneCommand = new RelayCommand(o => AddItemOne());
AddItemTwoCommand = new RelayCommand(o => AddItemTwo());
LogViewModel = new LogViewModel();
_currentView = LogViewModel;
}
private void AddItemOne()
{
LogViewModel.Logs.Add("Test");
}
private void AddItemTwo()
{
LogViewModel.Logs.Add("Test");
}
}
LogView.xaml
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
<Grid>
<ListView ItemsSource="{Binding Logs}" Background="Gray"/>
</Grid>
LogViewModel.cs
class LogViewModel
{
public ObservableCollection<string> Logs { get; set; }
public LogViewModel()
{
Logs = new ObservableCollection<string>();
}
}
Misc
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type local:LogViewModel}">
<local:LogView/>
</DataTemplate>
</Application.Resources>
And the RelayCommand
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Remove this from LogView.xaml:
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
It creates another instance of the LogViewModel instead of using the one that you create in the MainViewModel.
You should also replace the UserControl in MainWindow.xaml with a ContentControl that binds to the CurrentView property:
<ContentControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2" />
I'm new to UWP I am attempting to bind to an event in my ViewModel from a button flyout inside a listview that is shown on every item. I've looked at many solutions online and came up with the following code, it compiles fine but when I click the said Edit button nothing happens.
My ViewModel is available from the Page's context and not the Item's context
XAML
<ListView x:Name="MainListView"
ItemsSource="{x:Bind ViewModel.Devices, Mode=OneWay}"
SelectionMode="Multiple"
SelectionChanged="MainListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"></ColumnDefinition>
<ColumnDefinition Width=".4*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="2" Text="{Binding AssetNumber}"/>
<TextBlock Grid.Column="3" Text="{Binding SerialNumber}"/>
<TextBlock Grid.Column="4" Text="{Binding Model}"/>
<Button Grid.Column="1" Height="30" Width="30">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Edit" Icon="Edit"
Command="{Binding ElementName=MainListView,Path=DataContext.ViewModel.EditCommand}"
CommandParameter="{Binding}"/>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
View Model Class
public class MainPageViewModel
{
// Elements contained in the main listview
public ObservableCollection<Device> Devices = new ObservableCollection<Device>();
public MainPageViewModel()
{
DeviceProvider.Fill(ref Devices, 100);
EditCommand = new RelayCommand<Device>(EditDevice);
}
public RelayCommand<Device> EditCommand { get; set; }
private async void EditDevice(Device device)
{
// Code here that creates a dialog
}
}
The Device class
public class Device : INotifyPropertyChanged
{
private string assetNumber;
private string serialNumber;
private string model;
public string AssetNumber
{
get
{
return assetNumber;
}
set
{
assetNumber = value;
OnPropertyChanged();
}
}
public string SerialNumber
{
get
{
return serialNumber;
}
set
{
serialNumber = value;
OnPropertyChanged();
}
}
public string Model
{
get
{
return model;
}
set
{
model = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
The RelayCommand class
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute) : this(execute, null)
{
}
public RelayCommand(Action<T> execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Your code doesn't seem to have any problems. So it should work perfectly. But if not, I'd suspect MainPage.ViewModel member might not be defined properly. The property to be used in {Binding} must be "public" and must have "get" accessor.
public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
}
it compiles fine but when I click the said Edit button nothing happens.
The problem is that you bind wrong Path(Path=DataContext.ViewModel.EditCommand) for MenuFlyoutItem, please remove ViewModel field. And I have edited your code please refer the following.
<Page.DataContext>
<local:MainPageViewModel x:Name="ViewModel"/>
</Page.DataContext>
......
<Button
Grid.Column="1"
Width="30"
Height="30"
>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
Command="{Binding ElementName=MainListView, Path=DataContext.EditCommand}"
CommandParameter="{Binding}"
Icon="Edit"
Text="Edit"
/>
</MenuFlyout>
</Button.Flyout>
</Button>
My task: I want to bind textbox and button.
Although I found many topics about it I cannot manage my problem.
I have project: Client with WPF application WITH DEFAULT XAML no BINDING, which takes context from MenuWindow project, which is library. Inside MenuWindow project I have User Control WPF called: MenuProgram.
<UserControl x:Class="MenuWindow.MenuProgram"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MenuWindow"
mc:Ignorable="d"
d:DesignHeight="550" d:DesignWidth="780">
<UserControl.DataContext>
<local:MenuViewModel/>
</UserControl.DataContext>
<Grid Background="#FF6F6FA4">
<Label x:Name="lblTitle" Content="GUI Export Revit Data" HorizontalAlignment="Left" Margin="277,31,0,0" VerticalAlignment="Top" Height="50" Width="258" FontSize="24" FontWeight="Bold"/>
<Label x:Name="lblPrtdPath" Content="File prtd path" HorizontalAlignment="Left" Margin="200,176,0,0" VerticalAlignment="Top"/>
<Label x:Name="lblXmlPath1" Content="File xml path1" HorizontalAlignment="Left" Margin="200,222,0,0" VerticalAlignment="Top"/>
<Label x:Name="lblXmlPath2" Content="File xml path2" HorizontalAlignment="Left" Margin="200,266,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="tbxPrtd" HorizontalAlignment="Left" Height="23" Margin="302,176,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding PrtdFilePath}"/>
<TextBox x:Name="tbxXml1" HorizontalAlignment="Left" Height="23" Margin="302,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding XmlFilePath1}"/>
<TextBox x:Name="tbxXml2" HorizontalAlignment="Left" Height="23" Margin="302,266,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding XmlFilePath2}"/>
<Button x:Name="SayHi" Content="Start" HorizontalAlignment="Left" Margin="302,450,0,0" VerticalAlignment="Top" Width="174" Height="84" FontSize="22" Command="{Binding SayHi}" />
<Button x:Name="btnAbout" Content="About" HorizontalAlignment="Left" Margin="705,496,0,0" VerticalAlignment="Top" Width="55" Height="38" Command="{Binding SayHi}"/>
</Grid>
so I have
<UserControl.DataContext>
<mv:MenuViewModel/>
</UserControl.DataContext>
and with textBoxs or button I want to use binding.
in codeBehind this User Control there is nothing but default initialization.
In Project Menu there are:
MenuArguments.cs with mapping:
public string PrtdFilePath { get; set; }
public string XmlFilePath1 { get; set; }
public string XmlFilePath2 { get; set; }
RelayCommand:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MenuWindow
{
public class RelayCommand : ICommand
{
private readonly Func<Boolean> _canExecute;
private readonly Action _execute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<Boolean> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public Boolean CanExecute(Object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(Object parameter)
{
_execute();
}
}
}
and MenuViewModel.cs
namespace MenuWindow
{
public class MenuViewModel : INotifyPropertyChanged
{
public string gowno;
public MenuArguments _menuArgumenty;
public string PrtdFilePath
{
get { return _menuArgumenty.PrtdFilePath; }
set
{
_menuArgumenty.PrtdFilePath = value;
OnPropertyChanged("PrtdFilePath");
}
}
public string XmlFilePath1
{
get { return _menuArgumenty.XmlFilePath1; }
set
{
_menuArgumenty.XmlFilePath1 = value;
OnPropertyChanged("XmlFilePath1");
}
}
public string XmlFilePath2
{
get { return _menuArgumenty.XmlFilePath2; }
set
{
_menuArgumenty.XmlFilePath2 = value;
OnPropertyChanged("XmlFilePath2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public MenuViewModel()
{
_menuArgumenty = new MenuArguments();
}
public ICommand SayHi
{
get
{
return new RelayCommand(SayHiExcute, CanSayHiExcute);
}
}
private void SayHiExcute()
{
if (!MenuArgumentsExists(_menuArgumenty))
{
MessageBox.Show(string.Format("Hi {0} {1}!", _menuArgumenty.PrtdFilePath, _menuArgumenty.XmlFilePath1));
SavePerosn(_menuArgumenty);
}
else
{
MessageBox.Show(string.Format("Hey {0} {1}, you exists in our database!", _menuArgumenty.PrtdFilePath, _menuArgumenty.XmlFilePath1));
}
}
private void SavePerosn(MenuArguments _menuArgumenty)
{
//Some Database Logic
}
private bool CanSayHiExcute()
{
return !MenuArgumentsExists(_menuArgumenty);
}
private bool MenuArgumentsExists(MenuArguments _menuArgumenty)
{
//Some logic
return false;
}
}
}
When I start program debuger goes through binding properties. After window appears there is no reaction from binding. What do I do wrong? Please help me.
BR,
student Cenarius
Thanks for comments, answers to your comments:
#tabby - I want to bind textBoxes: PrtdFilePath, XmlFilePath1, XmlFilePath1 and button SayHi
#maulik kansara - You are right, I was trying some another methods and I didnt remove code. It should be only version with local.
#grek40 - My example works for one-project in solution for Window not for UserControl which is set in another project. Here is picture:
#mm8 - I expected by puting data ino textBoxes or clicking button to see breakpoint in:
public string PrtdFilePath
{
get { return _menuArgumenty.PrtdFilePath; }
set
{
_menuArgumenty.PrtdFilePath = value;
OnPropertyChanged("PrtdFilePath");
}
}
Finally, I think that code in XAML is problem. I was reading about parent-child relations with finding binding/viewmodel/path but I am confused and I dont know how to solve it. Please help me thanks You for all comments.
#grek40 here is Code in Main APP WPF, I add context from my MenuWindow.
This MainWindow WPF APP has default XAML.
public MainWindow()
{
InitializeComponent();
menuProgram = new MenuProgram();//User Control
sw = new SharedWindow();//WPF window
this.Close();
sw.Content = menuProgram.Content;// here I set context
sw.ShowDialog();
}
and XAML:
<Window x:Class="Client.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Whole code with Your change:
public partial class MainWindow : Window
{
private SharedWindow sw;
private MenuProgram menuProgram;
public MainWindow()
{
InitializeComponent();
menuProgram = new MenuProgram();
SetForContext();
}
private void SetForContext()
{
sw = new SharedWindow();
this.Close();
sw.Content = menuProgram;
sw.ShowDialog();
}
You need to set the UserControl as window Content, not the Content of UserControl:
sw.Content = menuProgram;// here I set context
/* Bad: sw.Content = menuProgram.Content; */
Your DataContext is assigned to the UserControl itself, so if you move the Content tree to a different Parent, it will no longer have its old DataContext.