getting listbox.selectedindex by clicking on checkbox - c#

this is my user template in listbox. i want to have "listbox.selectedindex" on clicking any checkbox of listbox. i want to knw of which row,checkbox is selected.like on click event of checkbox,it should focus the whole selected row.
<ListView x:Name="listbox3" Visibility="Visible" Margin="540,168,37,46" IsSynchronizedWithCurrentItem="True" BorderBrush="Black">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="200" >
<TextBlock Text="{Binding VmName}" Width="129" Visibility="Visible" />
<CheckBox x:Name="cb" IsThreeState="False" IsChecked="{Binding IsCheck, Mode=TwoWay}" Margin="6,0,18,6" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<CheckBox x:Name="cb1" IsThreeState="False" IsChecked="{Binding IsCheck1, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Without a good, minimal, complete code example that clearly illustrates the problem, it is difficult to know exactly what advice would be most useful to you. Without a clear problem statement, it's not even completely clear what you want the code to do.
However, if I understand correctly, you are displaying some data item type, using a corresponding DataTemplate object in your ListView. The user may toggle the checkboxes, and you want to update the currently selected item in the ListView, so that it is always the item containing the checkbox that was just toggled.
There are least a couple of reasonable ways to do that. In both cases, you will simply set the ListView.SelectedValue property to data item object reference corresponding to the CheckBox that is being modified.
The first way involves handling the Checked and Unchecked events on the CheckBox controls themselves, tracking back to the ListViewItem and then obtaining the data item for that ListViewItem.
First, you will need to write a handler to do the above:
private void cb_Checked(object sender, RoutedEventArgs e)
{
ListViewItem listViewItem =
GetVisualAncestor<ListViewItem>((DependencyObject)sender);
listbox3.SelectedValue =
listbox3.ItemContainerGenerator.ItemFromContainer(listViewItem);
}
private static T GetVisualAncestor<T>(DependencyObject o) where T : DependencyObject
{
do
{
o = VisualTreeHelper.GetParent(o);
} while (o != null && !typeof(T).IsAssignableFrom(o.GetType()));
return (T)o;
}
Note the helper method GetVisualAncestor<T>(). It uses VisualTreeHelper to walk the tree back to the ListViewItem object that contains the CheckBox control that was affected.
With this object found, the code then calls ItemContainerGenerator.ItemFromContainer() to find the actual data item object reference, and assigns this reference to the SelectedValue property.
Of course, for the handler to be useful, you need to subscribe it to the relevant Checked and Unchecked events. For example:
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel Orientation="Horizontal" Width="200" >
<TextBlock Text="{Binding VmName}" Width="129" Visibility="Visible" />
<CheckBox x:Name="cb" IsThreeState="False"
IsChecked="{Binding IsChecked1, Mode=TwoWay}"
Margin="6,0,18,6"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Checked="cb_Checked" Unchecked="cb_Checked"/>
<CheckBox x:Name="cb1" IsThreeState="False"
IsChecked="{Binding IsChecked2, Mode=TwoWay}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Checked="cb_Checked" Unchecked="cb_Checked"/>
</StackPanel>
</DataTemplate>
(Since you didn't provide a complete code example, in which the data item object class was included, I just wrote my own based on your question. I changed the property names so that they made more sense, i.e. IsChecked1 and IsChecked2. Feel free to use your own property names instead :) ).
The second way is a little more direct in one respect, but a little less direct in another respect. That is, assuming your data item object class implements INotifyPropertyChanged, you can subscribe to the PropertyChanged event for each data item object, and simply assign the sender of the event as the ListView.SelectedValue property.
This is more direct in that you don't have to add code that walks the visual tree back to some control's parent. But it's also less direct, in that you will need code that attaches the necessary event handler to each data item object.
An example of that might look like this:
List<DataItem> dataItems = new List<DataItem>
{
new DataItem { VmName = "sagar" },
new DataItem { VmName = "kaustubh" },
new DataItem { VmName = "gaurav" },
new DataItem { VmName = "abhi" },
};
listbox3.ItemsSource = dataItems;
PropertyChangedEventHandler handler =
(sender, e) => listbox3.SelectedValue = sender;
foreach (DataItem item in dataItems)
{
item.PropertyChanged += handler;
}
Note that in the above example, I assign the SelectedValue property on any property change. In my own code example, this is fine because the only properties that can change are the checkbox-related ones. And of course, this would also be fine if you want to select the corresponding ListView item on any property value change. But if you really only want to update on changes to the IsChecked1 and IsChecked2 properties, you'll need to look at the property name in the handler. E.g.:
PropertyChangedEventHandler handler = (sender, e) =>
{
if (e.PropertyName == "IsChecked1" || e.PropertyName == "IsChecked2")
{
listbox3.SelectedValue = sender;
}
}
Here is the DataItem class I wrote for the code examples for both approaches shown above:
class DataItem : INotifyPropertyChanged
{
private string _vmName;
private bool _isChecked1;
private bool _isChecked2;
public string VmName
{
get { return _vmName; }
set { _vmName = value; OnPropertyChanged(); }
}
public bool IsChecked1
{
get { return _isChecked1; }
set { _isChecked1 = value; OnPropertyChanged(); }
}
public bool IsChecked2
{
get { return _isChecked2; }
set { _isChecked2 = value; OnPropertyChanged(); }
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Related

Cannot execute Checked event when property change

I have a Combobox that bound a list of Contact defined in that way:
public List<Contact> Contacts { get;set; } = new List<Contact>();
public class Contact
{
public bool IsFavourite { get; set; }
public bool IsChecked { get; set; }
public string Name { get; set; }
}
NB: the Contact class implement INotifyPropertyChange, that I don't wrote in the example.
The Contacts list is bounded on the ComboBox in the following way:
<ComboBox ItemsSource="{Binding Contacts}"
ItemTemplate="{StaticResource CombinedTemplate}" />
where CombinedTemplate contains the following:
<DataTemplate x:Key="NormalItemTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Name}" Checked="Contact_Checked" Unchecked="Contact_Unchecked" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter" Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
</DataTemplate>
as you can see I bounded the IsChecked of CheckBox to the IsChecked property that is valorized behind code, so suppose that in the Contacts list is added this item:
var contact = new Contact();
contact.IsChecked = true;
contact.IsFavourite = true;
contact.Name = "Foo";
Contacts.Add(contact);
the Checked of the CheckBox should firing automatically 'cause the IsChecked is true, but I doesn't get this working.
What I did wrong? Thanks.
UPDATE
As suggested in the answer for fix this "bug" I should handle the Loaded event, so I did:
<ComboBox ItemsSource="{Binding Contacts}"
Loaded="ContactMenuComboBox_Loaded"
ItemTemplate="{StaticResource CombinedTemplate}" />
in the event I did:
private void ContactMenuComboBox_Loaded(object sender, RoutedEventArgs e)
{
foreach (var contact in Contacts)
{
if (contact.IsFavourite)
{
contact.IsChecked = true;
}
}
}
the IsChecked property is setted correctly, unfortunately the IsChecked event isn't firing.
Forget to say, if I put the code of ContactMenuComboBox_Loaded inside a button, and then press it, well the event IsChecked will firing.
This is a really weird situation.
UPDATE #2
This is the content of Checked event:
private void Contact_Checked(object sender, RoutedEventArgs e)
{
var contact = (sender as CheckBox).DataContext as CheckedListItem<Model.Contact>;
Task.Factory.StartNew(() =>
{
ContactController.GetLeagues(contact);
})
.ContinueWith((prevTask) =>
{
CheckTaskException(prevTask);
});
}
Update
I have checked with WPF and you are indeed right! The first binding evaluation does not fire the Checked event, only the subsequent calls do. This is in contrast to UWP, where the event is fired.
As a workaround, you could handle the Loaded event if you need to perform an action right after the value is first bound. However, if you are implementing the app as MVVM, you might be better off pushing the Checked logic in the setter of the IsChecked property.
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Name}"
Loaded="Contact_Loaded"
Checked="Contact_Checked" Unchecked="Contact_Unchecked" />
And in the Loaded method do something like:
var checkbox = (CheckBox)sender;
if ( checkbox.IsChecked ) Contact_Checked(this, e);
if ( !checkbox.IsChecked ) Contact_UnChecked(this, e);
Update 2
If you just want to execute the actions as soon as possible, you can indeed attach the Loaded event to your window and do the following:
foreach (var contact in Contacts)
{
if (contact.IsFavourite)
{
Task.Factory.StartNew(() =>
{
ContactController.GetLeagues(contact);
})
.ContinueWith((prevTask) =>
{
CheckTaskException(prevTask);
});
}
}
Of course to avoid code duplication, you could extract this code into a separate method with a Contact parameter.
Original answer
The problem is that your Contacts property is a normal List. In this case the data-bound controls will not update with any changes to that list after first binding. You should use ObservableCollection<T> instead:
public ObservableCollection<Contact> Contacts { get; set; } =
new ObservableCollection<Contact>();
In addition I don't see a point of having two data templates when one of them contains just a ContentPresenter for the other. You could simplify it into just NormalItemTemplate:
<ComboBox ItemsSource="{Binding Contacts}"
ItemTemplate="{StaticResource NormalItemTemplate}" />
Finally, the data-binding for Name is not correct, the Item. prefix should not be there:
<DataTemplate x:Key="NormalItemTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Name}"
Checked="Contact_Checked" Unchecked="Contact_Unchecked" />
</StackPanel>
</DataTemplate>

UWP Gridview binding to View Model

I'm using a GridView in a UserControl to display a five by four square of graphical buttons that allow selection of a Lesson.
This is in a Windows 8.1 Store App that I'm upgrading to Windows 10 UWP.
I previously used Tap and Right-Tap actions to select a Lesson or activate the CommandBar to perform related actions for a Lesson through the SelectionChanged event. However, there have been changes to how Interactions now work under Windows 10, I have been unable to get the Gridview to work at all with binding the SelectedItem to the selected LessonButton in the view model, nor the SelectionChanged and ItemClick events for such purposes. The Gridview selections behaviour doesn't work, as once an item is selected it is never deselected. So finally, I've taken a different tack and am trying Tap and Right-Tap events for the Gridview Items. However the issue is, that no matter which way I approach it, I can't get Binding to work correctly.
So I have an object called LessonButton:
public class LessonButton : INotifyPropertyChanged
{
//public LessonButton() { }
public LessonButton(SolidColorBrush inBackground, bool inComplete, double inHeight, int inNumber, bool inSelected, bool inStarted,
Status inState, double inWidth)
{
...
Started = inStarted;
...
}
...
private bool _started;
public bool Started
{
get { return _started; }
set { if (_started != value) { _started = value; OnPropertyChanged(); } }
}
...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
It is added to an observable collection in the View Model:
public class LessonsViewModel : INotifyPropertyChanged
{
public ObservableCollection<LessonButton> Lessons { get; } = new ObservableCollection<LessonButton>();
private LessonButton _selectedLessonButton;
public LessonButton SelectedLessonButton
{
get { return _selectedLessonButton; }
set { if (_selectedLessonButton != value) { _selectedLessonButton = value; OnPropertyChanged(); } }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In a user control I set the DataContext with:
<UserControl.DataContext>
<classes:LessonsViewModel/>
</UserControl.DataContext>
..and I then have a GridView defined as:
<GridView x:Name="LessonGridView" ItemContainerStyle="{StaticResource GridViewItemStyle}" ItemsSource="{Binding Lessons}"
SelectionMode="Single" IsItemClickEnabled="False" SelectedItem="{Binding Path=SelectedLessonButton, Mode=TwoWay}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid HorizontalChildrenAlignment="Left" MaximumRowsOrColumns="5" Orientation="Horizontal" VerticalChildrenAlignment="Top"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
With the GridView item format defined in a ControlTemplate as part of the GridViewItemStyle.
I've tried to access the LessonButton variables in various ways using Binding and xBind, but could only get the program to run with the ControlTemplate using this XAML:
<Image Grid.Row="1" Grid.Column="1" Width="{StaticResource BadgeSize}"
Height="{StaticResource BadgeSize}" HorizontalAlignment="Right" VerticalAlignment="Top"
Opacity="{Binding Started, Converter={StaticResource OpacityConverterTrueValueIsVisible}}"
Source="/Assets/SelectionButtonGroup/square310x310logobw.png" Stretch="Uniform"/>
The Converter simply returns a 1 or 0 depending upon the value of the bool Started.
Although this code work, it is not correct somehow and Visual Studio reports an unknown error and states it cannot find the Started property. In fact it can't find any of the properties of LessonButton and I've been unable to find the correct syntax for exposing them, even with x:Bind code such as:
{x:Bind LessonViewModel.Lessons.LessonButton.Selected}
..or versions thereof, using casting etc.
I'm using Visual Studio 2017 Enterprise, which reports the aforementioned errors and displays wavy lines over the entire ControlTemplate with an error where it cannot find another Converter artefact that isn't even related to this code.. which in itself, I find extremely irritating. Is it me or does the XAML Intellisence in VS seem very flaky, in that it gives up and reports false errors if it can't identify the root cause of a real one?
Ideally I'd like the Gridview SelectedItem to bind with the ViewModel. But even trying actions via Tap events I can't get the binding to correctly expose LessonButton properties in the ControlTemplate XAML.
Any help would be greatly appreciated.
You shouldn't be using the ItemContainerStyle to Bind your LessonButton variables to. The ItemContainerStyle is used to style the Item with selection marks, its hover and pressed states etc.
You should instead use a DataTemplate stored inside your UserControl's resources like so:
<Grid>
<Grid.Resources>
<DataTemplate x:Name="GridViewTemplate">
<TextBlock Text="{Binding LessonName}">
</DataTemplate>
</StackPanel.Resources>
<GridView x:Name="GridView"
ItemsSource="{Binding Lessons}"
ItemTemplate="{StaticResource GridViewTemplate}">
</GridView>
</Grid>
Then give your DataTemplate a name (above "GridViewTemplate") and set it as the ItemTemplate of your GridView.

Displaying text in ListBox while adding SearchResult object

I am adding Users from Active Directory to my ListBox.
The object I got from Active Directory is SearchResult and this is what I add to my ListBox. The problem is I don't know how to display as a text value of SearchResult object property.
ListBox displays "System.DirectoryServices.SearchResult" while I would like to have displayed "John Smith" (which is "cn" property in my SearchResult object)
Here is my code:
XAML:
<telerik:RadListBox Grid.Column="2" Grid.Row="2" SelectionMode="Multiple" x:Name="SearchListbox" Margin="5 5 5 0" Height="100"/>
<telerik:RadListBox Grid.Column="4" Grid.Row="2" SelectionMode="Multiple" x:Name="AddListbox" Margin="5 5 5 0" Height="100"/>
CS:
DirectorySearcher searcher = new DirectorySearcher(entry.Path)
{
Filter = "(&(cn*)(sn=*)(mail=*)(givenName=*))"
};
var results = searcher.FindAll();
foreach (SearchResult result in results)
{
SearchListBox.Items.Add(result);
}
I can't use ItemSource because I want to transfer object from one ListBox to another and with ItemSource I can't simply delete object from ListBox.
Any idea how to handle that?
UPDATE, SOLVED PROBLEM WITH NOT CHANGING ObservableCollection:
full working code:
private ObservableCollection<SearchResult> resultsSearch = new ObservableCollection<SearchResult>();
private ObservableCollection<SearchResult> resultsAdd = new ObservableCollection<SearchResult>();
public ObservableCollection<SearchResult> ResultsSearch
{
get { return resultsSearch; }
set { resultsSearch = value; }
}
public ObservableCollection<SearchResult> ResultsAdd
{
get { return resultsAdd; }
set { resultsAdd = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
protected virtual void OnCollectionChange(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
public void Add(SearchResult item)
{
this.ResultsSearch.Add(item);
this.OnCollectionChange(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, item));
}
public void Remove(SearchResult item)
{
this.ResultsSearch.Remove(item);
this.OnCollectionChange(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove, item));
}
To display the name of the user you have to set the ItemTemplate property:
<telerik:RadListBox x:Name="SearchListbox" Grid.Column="2" Grid.Row="2"
SelectionMode="Multiple" Margin="5 5 5 0" Height="100">
<telerik:RadListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Properties[cn]}"/>
</DataTemplate>
</telerik:RadListBox.ItemTemplate>
</telerik:RadListBox>
Best practice will be to create a ViewModel class around the SearchResult model. In this case your ViewModel will declare a FullName property that will access the dictionary in your model for the actual value:
SearchResultViewModel.cs
public string FullName
{
get { return searchResult.Properties["cn"];}
}
You should avoid tying down your View to your business logic. Using the above ViewModel the binding will look like Text="{Binding FullName}", this removes the necessity to create complicated binding.
I cant use ItemSource because I want to transfer object from one
ListBox to another and with itemSource I cant simply delete object
from ListBox.
Yes you can, just use an ObservableCollection as ItemsSource. The UI will be updated when you make changes to this collection.

C# MVVM: Binding a RadioButton to a boolean Property

I am quiet new to programming and am currently learning C# and the MVVM pattern.
I need to code a database tool for ChiliPlants for university.
There you should be able to add a new object to an ObservableCollection.
To add a new Item to this ObservableCollection a new Window opens. It looks like this:
Window Add
I now want the two RadioBoxes to be bound to a property called "HybridSeed". Which is defined in the ViewModel:
//Public Property HybridSeed
public bool HybridSeed
{
get { return ChiliModel.HybridSeed; }
set
{
if (ChiliModel.HybridSeed == value)
return;
ChiliModel.HybridSeed = value;
OnPropertyChanged("HybridSeed");
}
}
The RadioBox part of my View looks like this:
<RadioButton Grid.Row="5" Content="Ja" Grid.Column="1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<RadioButton Grid.Row="5" Content="Nein" Grid.Column="1" HorizontalAlignment="Left" Margin="89,10,0,0" VerticalAlignment="Top"/>
But how to bind the outcome of a user clicking on these RadioButtons to this HybridSeed Property? Important is that the outcome is a bool.
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 :)
If there is a simpler solution using a CheckBox or a ComboBox it would also be perfect. The most important thing is to have a nice user interface. Right now it only works with a TextBox where the user always has to write "True" or "False".
Solution:
I added the IsClicked Property in the "Yes" RadioButton to be bound to my boulean property with: IsClicked="{Binding HybridSeed}". Thanks to naslund for his fast answer :)
Just bind HybridSeed to the Yes-radiobutton. It will then either be true if the user has selected that or false if No-radiobutton has been selected (or if nothing has been selected). Binding to both buttons in this case is a bit redundant since the mechanism of radiobuttons takes care of it.
WPF:
<RadioButton Content="Yes" IsChecked="{Binding HybridSeed}" />
<RadioButton Content="No" />
<Label Content="{Binding HybridSeed}" ContentStringFormat="Value is: {0}" />
Logic:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
private bool hybridSeed;
public bool HybridSeed
{
get { return hybridSeed; }
set
{
hybridSeed = value;
OnPropertyChanged(nameof(HybridSeed));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

NotifySourceUpdated & Binding.SourceUpdated not working

My event below (OnSourceUpdated) is not getting handled.
XAML:
<StackPanel x:Name="MyStackPanel"
Orientation="Horizontal"
DockPanel.Dock="Top">
<TextBox Text="{Binding Side, Mode=TwoWay}"
Width="100"/>
<TextBlock Background="Yellow"
Text="{Binding Side, Mode=OneWay,
NotifyOnSourceUpdated=True}"
Width="100"
SourceUpdated="OnSourceUpdated"
Binding.SourceUpdated="OnSourceUpdated"/>
</StackPanel>
C#:
....
MyStackPanel.DataContext = new MyItemClass() { Side = "Test" };
....
private void OnSourceUpdated(Object sender, DataTransferEventArgs args)
{
var i = args.Property;
}
public class MyItemClass : INotifyPropertyChanged
{
private string _side;
public string Side
{
get { return _side; }
set
{
_side = value;
OnPropertyChanged("Side");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I have all the relevant settings done like NotifyOnSourceUpdated & SourceUpdated & Binding.SourceUpdated etc.
From msdn: Binding.SourceUpdated attached event occurs when a value is transferred from the binding target to the binding source, but only for bindings with the NotifyOnSourceUpdated value set to true
In the Binding of TextBlock, there is no value transfer from the binding target (TextBlock.Text) to the binding source (Side). Thus SourceUpdated cannot be fired.
Instead SourceUpdated can be fired on the first binding. Indeed here the target binding TextBox.Text can change the binding source (Side).
Maybe I'm missing something, but I'm thinking your approach to updating is a bit strange. Is there a reason you're not just going with
<TextBlock Text="{Binding foo, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" ... />
If you're just interested in updates coming from source, that's normally the way of doing it. Calling
OnPropertyChanged( "PropertyName" )
covers the rest.

Categories