I have a WPF/MVVM project in C #/FrameWork 4.0
In my view I have two ControlBox "NoRSAC" and "LieuRSAC"
<View:StateControlTextBox
x:Name="NoRSAC"
ReadOnly="{Binding IsReadOnly}"
ViewModelDataType="UtilisateurSaisieViewModel"
TableDataType="TUtilisateurDataTable"
Tag="{DynamicResource TELEPHONE}"
Text="{Binding UserVM.No_RSAC, Mode=TwoWay}" Margin="0" Canvas.Top="140" Width="185" VerticalAlignment="Stretch" />
<View:StateControlTextBox
x:Name="LieuRSAC"
ReadOnly="{Binding IsReadOnly}"
ViewModelDataType="UtilisateurSaisieViewModel"
TableDataType="TUtilisateurDataTable"
Tag="{DynamicResource TELEPHONE}"
Text="{Binding UserVM.Lieu_RSAC, Mode=TwoWay}" Margin="0" Canvas.Top="140" Width="185" VerticalAlignment="Stretch"/>
</Canvas>
And ControlComboBox "cmbFonction"
<View:StateControlComboBox
x:Name="cmbFonction"
ReadOnlyControlState="Disabled"
IsReadOnly="{Binding IsReadOnly}"
ViewModelDataType="UtilisateurSaisieViewModel"
TableDataType="TUtilisateurDataTable"
ItemsSource="{Binding ListeFonctions}"
SelectedValue="{Binding UserVM.Fonction, Mode=TwoWay}" Width="303" Margin="0" HorizontalAlignment="Left" Canvas.Left="97" Canvas.Top="108" />
I want to view the ControlBox "NoRSAC" and "LieuRSAC" when I select a particular valeure in the ComboBox "cmbFonction" and hide when it's another selected value
Thank you for your help
In the set method of the property Fonction, you can check the value and update another property that you should introduce in your view model and that is of type System.Windows.Visibility. In the following example, I call this property TextBoxVisibility:
public class UserVM : INotifyPropertyChanged
{
private Visibility _textBoxVisibility;
public Visibility TextBoxVisibility
{
get { return _textBoxVisibility; }
set
{
_textBoxVisibility = value;
OnPropertyChanged();
}
}
public string Fonction
{
get { return _fonction; }
set
{
_fonction = value;
OnPropertyChanged();
if (value == "Value A")
TextBoxVisibility = Visibility.Hidden;
else
TextBoxVisibility = Visibility.Visible;
}
}
// Other members omitted for sake of simplicity.
}
Please note that you need to implement INotifyPropertyChanged (directly or indirectly) so that the changes of the property values are forwarded to the bindings that can in turn update the dependency properties of the controls in your view.
Thus you must not forget to add an additional binding to all of your text boxes in your view. Here is an example for that, the important part is the binding on Visibility:
<View:StateControlTextBox
x:Name="NoRSAC"
ReadOnly="{Binding IsReadOnly}"
ViewModelDataType="UtilisateurSaisieViewModel"
TableDataType="TUtilisateurDataTable"
Tag="{DynamicResource TELEPHONE}"
Visibility="{Binding UserVM.TextBoxVisibility}"
Text="{Binding UserVM.No_RSAC, Mode=TwoWay}" Margin="0" Canvas.Top="140" Width="185" VerticalAlignment="Stretch" />
Related
Im building a WPF application and trying to stick to the MVVM pattern as much as possible. I have a list box with a data template inside of it that contains a TextBlock and Button. If the button within the data template is clicked it does not select the entire row, so I am unaware of what row it pertains to. I would like to grab the entire object and bind it to a property in the view model. Can I get some help or a workaround for this please that sticks to mvvm pattern.
List box with item template
<telerik:RadListBox Width="200" Height="150" HorizontalAlignment="Left" Margin="10" ItemsSource="{Binding ListOfSupplierInvoices}"
SelectedItem="{Binding SelectedSupplierInvoice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<telerik:RadListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" >
<TextBlock Text="{Binding InvoiceNumber}" HorizontalAlignment="Left" Margin="5" ></TextBlock>
<telerik:RadButton Height="20" >
<telerik:RadButton.Content>
<Image Source="/ImageResources/Misc/delete.png" Stretch="Fill" />
</telerik:RadButton.Content>
</telerik:RadButton>
</StackPanel>
</DataTemplate>
</telerik:RadListBox.ItemTemplate>
</telerik:RadListBox>
How it looks in the view:
As far as I understand your code, the button corresponds to a delete command, which means you want to delete the item associated with the button. In this case, the selection might not need to change, you just have to pass the current item to the delete command.
Add a Delete command to your view model like this:
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
Delete = new DelegateCommand(ExecuteDelete, CanExecuteDelete);
// ...other code.
}
public ICommand Delete { get; }
private void ExecuteDelete(object obj)
{
var invoiceItem = (InvoiceItem) obj;
// Use this only if you need the item to be selected.
// SelectedSupplierInvoice = invoiceItem;
// ...your delete logic.
}
private bool CanExecuteDelete(object obj)
{
// ...your can execute delete logic.
}
// ...other code.
}
Note that I introduced InvoiceItem as item type, because I do not know your item type, simply adapt it. The Delete command gets the current item passed as parameter. If you can always remove the item, there is no need in selecting it, as it is gone afterwards.
Otherwise, uncomment the line so the SelectedSupplierInvoice is set to the item which will automatically update the user interface through the two-way binding if you have implemented INotifyPropertyChanged correctly or derive from ViewModelBase which exposes the RaisePropertyChanged or OnPropertyChanged method, e.g.:
private InvoiceItem _selectedSupplierInvoice;
public InvoiceItem SelectedSupplierInvoice
{
get => _selectedSupplierInvoice;
set
{
if (_selectedSupplierInvoice == value)
return;
_selectedSupplierInvoice = value;
RaisePropertyChanged();
}
}
In your XAML wire the button to the Delete command on the DataContext of the RadListBox.
<telerik:RadButton Height="20"
Command="{Binding DataContext.Delete, RelativeSource={RelativeSource AncestorType={x:Type telerik:RadListBox}}}"
CommandParameter="{Binding}">
<telerik:RadButton.Content>
<Image Source="/ImageResources/Misc/delete.png" Stretch="Fill" />
</telerik:RadButton.Content>
</telerik:RadButton>
I have a problem for which I'm searching an explanation. It's similar to what's been discussed in WPF ComboBox SelectedItem Set to Null on TabControl Switch, but it's involving a lesser degree of binding and so should be open to simpler solutions. What I'm describing below is a simplified case I've built to reproduce and try to understand why the problem is arising.
So, the project is based on MVVM, and the main window has just a button labelled "Search", declared as follows:
<Button Margin="50,0,0,0" Width="150" Height="40" Content="Search" HorizontalAlignment="Left" Command="{Binding UpdateViewCommand}" CommandParameter="Search"/>
The code is bound to UpdateView :ICommand that, is defined as follows:
class UpdateViewCommand : ICommand
{
private MainViewModel viewModel;
public UpdateViewCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (parameter.ToString() == "Search")
{
viewModel.SelectedViewModel = new SearchViewModel();
}
}
}
This view overlaps with the main one in the upper part, leaving the "Search" button visible, as shown in the picture below:
The view includes a ComboBox and a "Go" button, declared as:
<ComboBox Name="SearchCriterion" Canvas.Left="128" Canvas.Top="14" Height="22" Width="257" Background="#FF66CCFF" BorderBrush="Black" SelectedIndex="0"
SelectedItem="{Binding QueryType, Mode=OneWayToSource}">
<ComboBoxItem FontFamily="Calibri" FontSize="14" Background="#FF66CCFF">
Author
</ComboBoxItem>
<ComboBoxItem FontFamily="Calibri" FontSize="14" Background="#FF66CCFF">
Title
</ComboBoxItem>
</ComboBox>
<Button Name="SearchButton" Height="22" Content="Go" Canvas.Left="390" Canvas.Top="44" Width="45" BorderBrush="Black"
FontFamily="Calibri" FontSize="14" Background="#FF0066FF" Command="{Binding ExecQueryCmd}" Foreground="White"/>
All the button does is getting the ComboBoxItem value bound in the ComboBox declaration through the variable QueryType and print it. QueryType is declared as:
private ComboBoxItem _queryType = new ComboBoxItem();
public ComboBoxItem QueryType
{
get { return _queryType; }
set
{
Globals.mylog.Trace("In SearchViewModel.QueryType");
_queryType = value;
OnPropertyChanged(nameof(QueryType));
}
}
Assuming this is clear, here is the problem I see. I start the program, click on "Search" and the SearchView appears. I play with the ComboBox, click "Go" and all is fine. I can do this several times, no problem.
Now I click on "Search" again. No apparent change (the view is already there), but if I click on "Go" an exception is raised because the variable is null (I'm running under Visual Studio, so I can easily check). Note that if, instead of clicking "Go" right after clicking on "Search", I click on the ComboxBox and change its value before, everything works fine.
Can anyone explain me why this is happening, and how I can solve it?
Thanks
You never explicitly assigned a value to QueryType in the constructor of SearchViewModel, so the value in querytype was depending on the UI to update it.
A better way is to have the selectedvalue come from the viewmodel (and not have ui elements in tour viewmodels as I mentionned in the comments).
What I changed to make it works:
In SearchViewModel:
/// <summary>
/// Selected option to search by (it is now a string)
/// </summary>
private string _queryType;
public string QueryType
{
get { return _queryType; }
set
{
Globals.mylog.Trace("In SearchViewModel.QueryType");
_queryType = value;
OnPropertyChanged(nameof(QueryType));
}
}
/// <summary>
/// List of options to search by
/// </summary>
public ObservableCollection<string> Queries { get; set; }
public SearchViewModel()
{
Globals.mylog.Trace("In SearchViewModel");
//Initialization ofthe list of options
Queries = new ObservableCollection<string> { "Author", "Title" };
//Initialization of the selected item
this.QueryType = Queries.FirstOrDefault();
ExecQueryCmd = new RelayCommand(ExecuteQuery, CanExecuteQuery);
}
In SearchView:
<--The combobox is now bound to the list in the ViewModel(the data is stored in the viewmodels and the view is only responsible for displaying it) -->
<Canvas Width="517" Height="580" Background="#FFCCFF99">
<ComboBox Name="SearchCriterion" Canvas.Left="128" Canvas.Top="14" Height="22" Width="257" ItemsSource="{Binding Queries}" Background="#FF66CCFF" BorderBrush="Black"
SelectedItem="{Binding QueryType, Mode=TwoWay}">
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type ComboBoxItem}}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Background" Value="#FF66CCFF"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<Button Name="SearchButton" Height="22" Content="Go" Canvas.Left="390" Canvas.Top="44" Width="45" BorderBrush="Black"
FontFamily="Calibri" FontSize="14" Background="#FF0066FF" Command="{Binding ExecQueryCmd}" Foreground="White"/>
</Canvas>
I have a problem. I have a car class with string brand, string model,..
I also have a view with a ComboBox containing Data. When I select an item from the ComboBox "carBrand" or the ComboBox "carModel" and click on a button, I want to create a new car object. But after clicking on the button, the carBrand.SelectedValue.ToString() is delivering a Null value, same for carModel, although I selected an item from the ComboBox.
In my VMClass:
Add a1 = new Add();
c_car m1 = param as c_car;
a1.DataContext = m1;
a1.ShowDialog();
if (a1.DialogResult.HasValue && a1.DialogResult.Value)
{
m1.c_brand = a1.carBrand.SelectedValue.ToString(); //causes NullReferenceException
m1.c_model = a1.carModel.SelectedValue.ToString(); //causes NullReferenceException
m1.c_year = a1.carYear.Content.ToString(); //this works perfectly
m1.c_km = Int32.Parse(a1.carKm.Content.ToString()); //this also works properly
//...
}
Now my View Class:
<!--CarModel ComboBox-->
<ComboBox x:Name="carModel" Style="{StaticResource combobox}" Grid.Column="1"
Margin="20,15,17,14"
ItemsSource="{Binding ModelSelectedBrand}" DisplayMemberPath="c_model" MouseLeave="carModel_MouseLeave"
Grid.Row="2" VerticalAlignment="Center" Height="30" MouseDoubleClick="carModel_MouseDoubleClick">
</ComboBox>
<!--CarYear EditableLabel-->
<Label x:Name="carYear" Content="{Binding ElementName=carModel, Path=SelectedValue.c_year}" Margin="20,14,17,14"
Style="{StaticResource EditableLabelStyle}" Foreground="White"
Grid.Column="1" Grid.Row="5" VerticalAlignment="Center" Height="30">
</Label>
<!--CarKM EditableLabel-->
<Label x:Name="carKm"
Content="{Binding ElementName=carModel, Path=SelectedItem.c_km}" Style="{StaticResource EditableLabelStyle}"
Margin="20,14,17,14"
Grid.Column="1" Grid.Row="3" Foreground="White" VerticalAlignment="Center" Height="30">
</Label>
I hope someone can help me fixing this issue.
Thanks in advance!
So the simple answer (I think, I haven't tested yet) is that there isn't a SelectedValuePath set on your ComboBox (As stated by vesan in the comments).
This means that SelectedValue will always be null.
You could use SelectedItem to return the selected Car and then get the property from that or just set the SelectedValuePath on the ComboBox.
Now, this could of course be made better by using bindings but that is up to you whether you want to implement this.
I have a TextBox which needs to be enabled / disabled programmatically. I want to achieve this using a binding to a Boolean. Here is the TextBox XAML:
<TextBox Height="424" HorizontalAlignment="Left"
Margin="179,57,0,0" Name="textBox2"
VerticalAlignment="Top" Width="777"
TextWrapping="WrapWithOverflow"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
AcceptsReturn="True" AcceptsTab="True"
Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=TextBoxEnabled}"/>
Notice the Text property is bound as well; it is fully functional, which makes me think it is not a DataContext issue.
However, when I call this code:
private Boolean _textbox_enabled;
public Boolean Textbox_Enabled
{
get { return _textbox_enabled; }
set
{
OnPropertyChanged("TextBoxEnabled");
}
}
It does not work. To give further information, the TextBox_Enabled property is changed by this method:
public void DisabledTextBox()
{
this.Textbox_Enabled = false;
}
..which is called when a key combination is pressed.
Here are your little typos!
private Boolean _textbox_enabled;
public Boolean TextboxEnabled // here, underscore typo
{
get { return _textbox_enabled; }
set
{
_textbox_enabled = value; // You miss this line, could be ok to do an equality check here to. :)
OnPropertyChanged("TextboxEnabled"); //
}
}
Another thing for your xaml to update the text to the vm/datacontext
Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding TextBoxEnabled}"/>
I'm a bit puzzled:
this works:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Rol" />
<ComboBox ItemTemplate="{StaticResource listRollen}"
Height="23" Width="150"
SelectedItem="{Binding Path=SelectedRol, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding Path=allRollen, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
and the property for SelectedRol is:
public TblRollen SelectedRol
{
get { return _selectedRol; }
set
{
if (_selectedRol != value)
{
_selectedRol = value;
OnPropertyChanged("SelectedRol");
}
}
}
But this doesn't work:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Soort" />
<ComboBox ItemTemplate="{StaticResource listSoorten}"
Height="23" Width="150"
ItemsSource="{Binding Path=allSoorten}"
SelectedItem="{Binding Path=SelectedProduct, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
with following property SelectedProduct:
public TblProduktSoorten SelectedProduct
{
get { return _selectedPSoort; }
set
{
if (_selectedPSoort != value)
{
_selectedPSoort = value;
OnPropertyChanged("SelectedProduct");
}
}
}
somewhere in my code I set SelectedProduct = p.TblProduktSoorten and while debugging, I see the property gets set correctly...
Combobox inside a DataGrid?
If the combobox is in a DataGrid you must add this :
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged
See this : https://stackoverflow.com/a/5669426/16940
Try to use not selected item but value path look at the code sample
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" Grid.Row="1" Visibility="Visible" Canvas.Left="10" Canvas.Top="24" Margin="11,6,13,10">
</ComboBox>
the binding property is
public ObservableCollection<Project> Projects
{
get { return projects; }
set
{
projects = value;
RaisePropertyChanged("Projects");
}
}
This might be related to the fact that apparently attribute order does matter, in your second case the ItemsSource and SelectedItem declarations are swapped.
If you set the SelectedProduct property when SelectedProduct is changed in the property changed event handler, you need to set this property asynchronously.
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedProduct")
App.Current.Dispatcher.InvokeAsync(() => SelectedProduct = somevalue);
}
My problem was caused by my own tired brain. Same symptom, maybe it will kick you into seeing your problem.
Setting the SelectedItem must be given an item in the List!! (duhh) Normally this happens naturally but I had a case I got a "Role" from another service (Same object type) and was trying to set it and expecting the combobox to change! ;(
instead of -
Roles = roles;
CurrentRole = role;
remember to do this -
Roles = roles;
CurrentRole = roles.FirstOrDefault(e=> e.ID == role.ID); //(System.Linq)
I don't know if you fixed it yet, but I encountered the same issue today.
It was fixed by making sure the collection for selecteditems was an ObservableCollection.
I think this problem is caused by the type of ItemSource and SelectedItem is mitchmatched.
For example, if the ItemSource is binded to a List of int and the SelectedItem is binded to a string. If you set selected item to null or empty string, the combobox cannot know what item is selected. So the combobox will show nothing.
This might be old but I have not seen the trick that did it for me; I had to add NotifyOnSourceupdate=true to my SelectedItem in the ComboBox
This had me stumped for a while inside a DataGrid, using SelectedItem. Everything was fine but I am deserializing the app state which loads the items and also has a selected item. The collection was there but the selected isn't actually visible until I used the Text="{Binding Path=Score.SelectedResult.Offset}"
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ToolTip="Score offset results"
ItemsSource="{Binding Score.SearchResults,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Score.SelectedResult, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=Score.SelectedResult.Offset}"
SelectedValuePath="Offset"
DisplayMemberPath="Offset"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>