My code is as below.
<ListBox x:Name="lstBoxMarket" BorderThickness="0" Height="Auto" HorizontalAlignment="Center" Width="200" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox IsChecked="{Binding Checked}" CommandParameter="{Binding MarketId}" Tag="{Binding MarketId}" Content="{Binding Market}" Foreground="#FF3D66BE" Name="chkMarket"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to access the selected and deselected checkboxes in the list on click of save button . I am unable to access chkMarket straight away. Can anyone help?
Starting from your code I tried something like that
// find all T in the VisualTree
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent)
where T : DependencyObject
{
List<T> foundChilds = new List<T>();
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
T childType = child as T;
if (childType == null)
{
foreach(var other in FindVisualChildren<T>(child))
yield return other;
}
else
{
yield return (T)child;
}
}
}
Then in your MainWindow
private void button1_Click(object sender, RoutedEventArgs e)
{
// find all checkboxes in my window
IEnumerable<CheckBox> myBoxes = FindVisualChildren<CheckBox>(this);
int numChecked = 0;
foreach(CheckBox cb in myBoxes)
{
if(cb.Name != "chkMarket")
continue;
if (cb.IsChecked == true)
numChecked++;
}
MessageBox.Show("Checked items = " + numChecked);
}
My viewmodel code is
public class ViewModel
{
public ViewModel()
{
_persons = new ObservableCollection<Person>();
_persons.Add(new Person() { Name = "Paul", Checked = false });
_persons.Add(new Person() { Name = "Brian", Checked = true });
}
private ObservableCollection<Person> _persons;
public ObservableCollection<Person> Persons
{
get { return _persons; }
}
}
public class Person
{
public String Name { get; set; }
public Boolean Checked { get; set; }
}
You should be able to see the message "Checked items=1".
Hope this helps
Since it was 2 way binding i could access the values selected by the checkboxes from the item source of listbox.
DataTable lstBoxMarketItemSourceDT = ((DataView)lstBoxMarket.ItemsSource).ToTable();
"Checked" column in the data table retrieved gives the updated check box values.
Related
I' ve got an error when I attempt to update integer parameter in my database. If I changed it +1/-1 everything fine but changing it more (+5, +7, -4) save +1/-1 step only (example: change 5 to 10 and load it from DB - get 6).
Objects of drug_storage class are in a BindingList. I trace changes using ListChanged method. I use integer updown counter binded to drug_quantity parameter in two way mode; also use not mapped integer tag property of an object to store information about previous number of parameter. If counter change this parameter tag != parameter and DB updates
XAML:
<UserControl.Resources>
<DataTemplate x:Key="ComboBoxDataTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding is_selected, Mode=TwoWay}" x:Name="checkbox" Margin="3" Checked="SelectComboBoxItem" Unchecked="SelectComboBoxItem" />
<TextBlock Text="{Binding public_drug_name}" Margin="2" />
<lc1:IntegerUpDown x:Name="iudQuantity" Minimum="1" Increment="1" Value="{Binding drug_quantity, Mode=TwoWay}" Margin="3"
Visibility="{Binding IsChecked, ElementName=checkbox, Converter={StaticResource BooleanToVisibilityConverter}}" ValueChanged="SelectComboBoxItem" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<ComboBox ItemsSource="{Binding InfusionDrugList}" Margin="5" ItemTemplate="{StaticResource ComboBoxDataTemplate}"
SelectedItem="{Binding SelectedDrug, Mode=OneWayToSource}">
XAML.CS (if something in data template is clicked comboboxitem becomes selected and set value of SelectedDrug what triggers viewmodel code:
private void SelectComboBoxItem(object sender, RoutedEventArgs e)
{
var v = FindVisualParent<ComboBoxItem>(sender as UIElement);
if (v != null) { v.IsSelected = true; } // some checkboxes can become selected automatically at the moment of druglist loading and v would be null
}
public static T FindVisualParent<T>(UIElement element) where T : UIElement
{
UIElement parent = element; while (parent != null)
{
T correctlyTyped = parent as T; if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
private void SelectComboBoxItem(object sender, RoutedPropertyChangedEventArgs<int> e)
{
var v = FindVisualParent<ComboBoxItem>(sender as UIElement);
if (v != null) { v.IsSelected = true; } // some updowns can become selected automatically at the moment of druglist loading and v would be null
}
VIEW MODEL:
In Constructor:
SelectedDrugs = new BindingList<drug_storage>();
SelectedDrugs.RaiseListChangedEvents = true;
SelectedDrugs.ListChanged += ProcessSelectedDrugCommands;
Later:
` public BindingList<drug_storage> SelectedDrugs { get; set; }
private drug_storage selectedDrug;
public drug_storage SelectedDrug
{
get { return selectedDrug; }
set
{
selectedDrug = value;
NotifyPropertyChanged();
AddDrugForProcessing();
}
}
private void AddDrugForProcessing()
{
if (SelectedDrug != null && !SelectedDrugs.Contains(SelectedDrug))
{
SelectedDrugs.Add(SelectedDrug);
}
}
private void ProcessSelectedDrugCommands(object sender, ListChangedEventArgs e)
{
.... //code to add and remove objects to DB
if (e.ListChangedType == ListChangedType.ItemChanged)
{
int position = e.NewIndex;
drug_storage drug = SelectedDrugs[position];
if (drug.is_selected && drug.tag != drug.drug_quantity) //when user changed number in updown counter
{
ChangeNumberOfDrugInDB(drug);
}
void ChangeNumberOfDrugInDB (drug_storage drug)
{
int procedure_id = SelectedInListProcedure.PMR_Id;
int drug_id = SelectedDrug.drug_id;
using (var db = new MainEntityModel())
{
var query = (from d in db.PMR_infusions_drugs
where d.PMR_Id == procedure_id && d.drug_id == drug_id
select d).Single();
query.drug_quantity = drug.drug_quantity;
db.SaveChanges();
drug.tag = drug.drug_quantity;
}
}}
I believe every time user change integerupdown binded to drug_quantity parameter it triggers DB update and then updates tag property but it looks like after inintial 1-integer-step updating drug.tag already becomes equal to final drug_quantity value. Can it be so?
Thanks
ADDITIONALY:
EF Class:
public partial class drug_storage
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public drug_storage()
{
PMR_infusions_drugs = new HashSet<PMR_infusions_drugs>();
}
[Key]
public int drug_id { get; set; }
[Required]
[StringLength(50)]
public string drug_name { get; set; }
[NotMapped]
public bool is_selected { get; set; }
[NotMapped]
private int quantity = 1;
[NotMapped]
public int drug_quantity { get { return quantity; } set { quantity = value; } }
[NotMapped]
public int tag = 0;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PMR_infusions_drugs> PMR_infusions_drugs { get; set; }
}
My WPF DataGrid is:
<dg:DataGrid Name="datagrid1" Grid.RowSpan="1" VerticalAlignment="Stretch" Grid.ColumnSpan="2">
<dg:DataGrid.Columns >
<dg:DataGridTemplateColumn>
<dg:DataGridTemplateColumn.Header>
<CheckBox Content=" Slect All" x:Name="headerCheckBox" />
</dg:DataGridTemplateColumn.Header>
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="chkSelectAll" Margin="45 2 0 0"
IsChecked="{Binding IsChecked, ElementName=headerCheckBox,
Mode=OneWay}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
Also Dynamicaly I am populating the data to the datgrid.In xaml.cs file I written the below given code for deleting the selected row from the data grid but it throwing the error at line
DataGridRow item =(DataGridRow) datagrid1.ItemContainerGenerator.ContainerFromItem(datagrid1.Items[j]);
So Please have a look in to the below given code which I written for doing the same.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
for (int j = 0; j < datagrid1.Items.Count; j++)
{
DataGridRow item =(DataGridRow) datagrid1.ItemContainerGenerator.ContainerFromItem(datagrid1.Items[j]);
CheckBox ckb = (CheckBox)GetVisualChild<CheckBox>(item);
if (ckb.IsChecked.Value)
{
DataRowView drv = (DataRowView)datagrid1.Items[j];
//delete the row- updating to the database
}
}
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Please let me know if am wrong.
Here is how I would do this. Implement an ObservableCollection of your class that inherits INotifyPropertyChanged. INotifyPropertyChanged will be used in case we want to update items in the collection.
First the xaml for the GridView
<DataGrid x:Name="gvMain" AutoGenerateColumns="True" HorizontalAlignment="Left"
VerticalAlignment="Top" Height="300" Width="300"></DataGrid>
Our class of items
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string firstName { get; set; }
private string lastName { get; set; }
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
PropertyChangedEvent("FirstName");
}
}
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
PropertyChangedEvent("LastName");
}
}
private void PropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Next the WPF window
public ObservableCollection<MyClass> gridData { get; set; }
public MainWindow()
{
InitializeComponent();
gridData = new ObservableCollection<MyClass>();
gvMain.ItemsSource = gridData;
}
Test to add, change, delete items in the collection
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
gridData.Add(new MyClass() { FirstName = "John", LastName = "Smith" });
}
private void btnChange_Click(object sender, RoutedEventArgs e)
{
gridData[0].FirstName = "Meow Mix";
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
//using List to use .ForEach less code to write and looks cleaner to me
List<MyClass> remove = gridData.Where(x => x.LastName.Equals("Smith")).ToList();
remove.ForEach(x => gridData.Remove(x));
}
Any changes you want to make will be done with gridData.
Today I am stucked in very basic concept again. What is the mistake I am doing.
I have XAML like
<ComboBox ItemsSource="{Binding MyItems}" Height="40" Width="200" SelectedIndex="0"
SelectedItem="{Binding MySelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding MySelectedItem.Name}"/>
<Button Content="Test" Grid.Row="1" Click="Button_Click_1"/>
My ModelView looks like
public class MainViewModel : DependencyObject,INotifyPropertyChanged
{
private MyModel mySelectedItem;
public MyModel MySelectedItem
{
get
{
return mySelectedItem;
}
set
{
if (value != mySelectedItem)
{
mySelectedItem = value;
RaisePropertyChange("MySelectedItem");
}
}
}
public IList<MyModel> MyItems
{
get
{
return new List<MyModel>() {new MyModel(){Name="A"},
new MyModel(){Name="B"},
new MyModel(){Name="C"}};
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange(string name)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
One MyItems property and one SelectedItem property
and Click handler like
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Random r = new Random();
int icnt = r.Next(0,3);
model.MySelectedItem = model.MyItems[icnt];
}
I found that TextBlock.Text is updating but Combobox selected item is not updating. I try to dig out the reason and found that if I execute code below
MyModel prevItem = model.MyItems.Where((m) => m.Name.Equals("A")).FirstOrDefault();
MyModel newItem = model.MyItems.Where((m) => m.Name.Equals("A")).FirstOrDefault();
bool result = prevItem.Equals(newItem);
The value is always false. But why, why I am getting the new reference to same object from collection.
How can resolve this issue.
Thanks
you are getting a new reference because each time the binding mechanism will ask for MyItems you will create a new list.
try creating it once and use observable collection
You need to modify your MyItems code. You are getting new list every time. Try this out.
private List<MyModel> _myItems;
public IList<MyModel> MyItems
{
get
{
if (_myItems == null)
{
myItems = new List<MyModel>();
myItems.Add(new MyModel() { Name = "A" });
myItems.Add(new MyModel() { Name = "B" });
myItems.Add(new MyModel() { Name = "C" });
}
return _myItems}
}
}
I am trying to implement a list that contains items of a certain type, a Session. Each Session contains a list that contains the type Note. I want to display these Notes in the list under their respective Session header.
Currently I have tried two different methods. The first way was to use ItemsControls as ControlTemplate for the ListBoxItems. This is what I used in the picture below and it is how I want the list to look like. Each red rectangle shows a Session, the items below the header are the Notes. The problem then is that the selection from the ListBox selects ItemsControls instead of each separate Note.
The other way I tried to implement the list is to give each Note a property of which Session it belongs to in order to use a GroupStyle on the ListBox. If I then set the ItemsSource of the ListBox to a list of Notes instead of Sessions I'll get a list that looks like the picture and that has selection of notes. The problem now is that I want the list to show Sessions that doesn't contain any Notes as well.
Does anyone know what I should use to implement a list with selection and that works the way I have described?
MainWindow.xaml:
<TreeView ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Session}" ItemsSource="{Binding Path=Notes}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Note}">
<Expander Header="{Binding Path=Notek}">
<TextBlock Foreground="Red" Text="{Binding Path=Details}" />
</Expander>
</DataTemplate>
</TreeView.Resources>
</TreeView>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Session> sessions = new List<Session>();
for (int i = 0; i < 5; i++)
{
List<Note> notes = new List<Note>();
for (int j = i * 5; j < (i + 1) * 5; j++)
{
Note note = new Note()
{
Notek = string.Format("Note {0}", j),
Details = string.Format("Note j = {0}{1}j*j = {2}", j, System.Environment.NewLine, j*j)
};
notes.Add(note);
}
Session session = new Session()
{
Name = string.Format("Session # {0}", i),
Notes = notes
};
sessions.Add(session);
}
DataContext = sessions;
}
}
public class Session
{
public string Name { get; set; }
public List<Note> Notes { get; set; }
}
public class Note
{
public string Notek { get; set; }
public string Details { get; set; }
}
I think that you can style your HierarchicalDataTemplate as you want. I just show you the example. I think its easier rather than ItemsControl with event handlers.
To create the answer I will assume the following data model:
class Session
{
public IEnumerable<Note> Notes { get; }
}
class Note { }
This requires some coding to sync up the list boxes. I have created an attached property called 'ListBoxGroup'. All listboxes with the same group name can only have a single shared selected item. It is quite a lot of code so it's at the bottom.
Important to note: The listboxgroup for a listbox cannot be changed after originally set, and it doesn't support removal of items, doesn't check for nulls etc. So if you need to change sessions at runtime you should remove items from their groups, check if a listbox is removed from the visual tree, etc.
First the XAML for the page:
xmlns:local="clr-namespace:YourApplication.YourNamespace"
<!-- ItemsControl does not have selection -->
<ItemsControl ItemsSource="{Binding SessionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- Header for the session -->
<Border Background="Gray">
<TextBlock Text="{Binding Name}" />
</Border>
<!-- listbox for notes -->
<ListBox ItemsSource="{Binding Notes}" local:ListBoxGroup.GroupName="Group1">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- Template for a single note -->
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Below is C# code for the ListBoxGroup property:
public static class ListBoxGroup
{
public static string GetGroupName(DependencyObject obj)
{
return (string)obj.GetValue(GroupNameProperty);
}
public static void SetGroupName(DependencyObject obj, string value)
{
obj.SetValue(GroupNameProperty, value);
}
// Using a DependencyProperty as the backing store for GroupName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GroupNameProperty =
DependencyProperty.RegisterAttached("GroupName", typeof(string), typeof(ListBoxGroup), new UIPropertyMetadata(null, ListBoxGroupChanged));
private static Dictionary<string, List<ListBox>> _listBoxes = new Dictionary<string, List<ListBox>>();
private static void ListBoxGroupChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
string newValue = e.NewValue as string;
ListBox listBox = obj as ListBox;
if (newValue == null || listBox == null) return;
if (_listBoxes.ContainsKey(newValue))
{
_listBoxes[newValue].Add(listBox);
}
else
{
_listBoxes.Add(newValue, new List<ListBox>() { listBox });
}
listBox.SelectionChanged += new SelectionChangedEventHandler(listBox_SelectionChanged);
listBox.PreviewKeyUp += new System.Windows.Input.KeyEventHandler(listBox_KeyUp);
}
static void listBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
ListBox listBox = sender as ListBox;
if (e.Key == System.Windows.Input.Key.Up && listBox.SelectedIndex == 0)
{
//move to previous
string groupName = GetGroupName(listBox);
List<ListBox> group = _listBoxes[groupName];
int senderIndex = group.IndexOf(listBox);
if (senderIndex != 0)
{
listBox.SelectedItem = null;
ListBox beforeSender = group[senderIndex - 1];
int index = beforeSender.Items.Count - 1;
beforeSender.SelectedIndex = index;
var container = beforeSender.ItemContainerGenerator.ContainerFromIndex(index);
(container as FrameworkElement).Focus();
}
}
else if (e.Key == System.Windows.Input.Key.Down
&& listBox.SelectedIndex == listBox.Items.Count - 1)
{
//move to next
string groupName = GetGroupName(listBox);
List<ListBox> group = _listBoxes[groupName];
int senderIndex = group.IndexOf(listBox);
if (senderIndex != group.Count - 1)
{
listBox.SelectedItem = null;
ListBox afterSender = group[senderIndex + 1];
afterSender.SelectedIndex = 0;
var container = afterSender.ItemContainerGenerator.ContainerFromIndex(0);
(container as FrameworkElement).Focus();
}
}
}
static void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
ListBox listBox = sender as ListBox;
string groupName = GetGroupName(listBox);
foreach (var item in _listBoxes[groupName])
{
if (item != listBox)
{
item.SelectedItem = null;
}
}
}
}
}
I have a listbox containing checkboxes. I want to get all the checkbox checked items content in a string array. How can I get this?
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="TrackingView1" Margin="9,0,2,5">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="fileNameLinkButton" Content="{Binding BindsDirectlyToSource=True}" FontFamily="Segoe WP Semibold" Foreground="Black" FontSize="20" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
You can use VisualTreeHelper to retrieve the datatemplate items,
use this method to get the first item of datatemplate
//method for finding first element of the listbox data template
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{ return (T)child; }
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
then use this code in the event you want to, for example on a button click
int itemsCount = this.TrackingView1.Items.Count;
List<string> myList = new List<string>();
for (int i = 0; i < itemsCount; i++)
{
ListBoxItem item = (ListBoxItem)this.TrackingView1.ItemContainerGenerator.ContainerFromIndex(i);
CheckBox tagregCheckBox = FindFirstElementInVisualTree<CheckBox>(item);
if((bool)tagregCheckBox.IsChecked)
myList.Add(tagregCheckBox.Content.ToString());
}
In a classic TextBook approach, You could bind "TrackingView1" to a IList, where PersonClass looks some thing like
public class PersonClass
{
public string Name { get; set; }
public bool IsSelected { get; set; }
public PersonClass(string name, bool isSelected)
{
this.Name = name;
this.IsSelected = isSelected;
}
}
and at the point where you want to collect your data,
string[] checkedNames = (from name in Names
where name.IsSelected
select name.Name).ToArray();
I would actually avoid the ToArray(), and accessing the UI directly