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.
Related
I implemented a ListBox using this and this. I bind my actual list of 29 objects to it and it works well.
In XAML:
<ListBox Name="WBauNrList" ItemsSource="{Binding}" Grid.Row="7" Grid.Column="2" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" Height="100" >
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding Baunr}" IsChecked="{Binding IsChecked,Mode=TwoWay}"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In code:
datenpunktList = new ObservableCollection<Datenpunkt>();
foreach (var d in WerkstattList.DistinctBy(p => p.lokNr))
{
var newd = new Datenpunkt() { Baunr = d.lokNr };
datenpunktList.Add(newd);
}
WBauNrList.ItemsSource = datenpunktList;
But the problem:
I want to have a select-all CheckBoxes to able the user to select and deselect all items. It works strangely!
After checking the selectAll CheckBox, all items will be checked that is not in the scope of the scrollbar (the list is scrolled), then I should scroll down and up to see that all items are checked.
XAML:
<CheckBox Name="selectAll" Click="selectAll_Click" >Secelct all</CheckBox>
Code:
private void selectAll_Click(object sender, RoutedEventArgs e)
{
foreach (Datenpunkt item in WBauNrList.Items)
{
item.IsChecked = true ;
}
}
I don't have any idea what to do.
Thanks in advance, Mo
Your property IsChecked implementation could look like this.
public class Datenpunkt : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
Notify("IsChecked");
}
}
}
Have a look at the MSDN INotifyPropertyChanged page for more information.
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;
}
i have a view that have a list view with data template
i need to set style on the selected item
but i need also when the selected item is been changed from the code it modify the selected item in the view
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Width="300" Height="50" TextAlignment="Center"/>
<ListView Grid.Row="1" ItemsSource="{Binding List, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate >
<Border BorderThickness="1" BorderBrush="White">
<Grid Height="20" Width="30" >
<TextBlock Text="{Binding Name}"/>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
there is a list view and textblock
i need when the selectedItem changed it changed the the background of the selected item
here is the viewmodel
public class MainViewModel : ViewModelBase
{
private Item selectedItem;
public ObservableCollection<Item> List { get; set; }
string text;
public string Text
{
get { return text; }
set
{
text = value;
OnPropertyChanged("Text");
}
}
public Item SelectedItem
{
get { return selectedItem; }
set{
if (value.Name != "Test1")
{
selectedItem = value;
Text = value.Name;
}
else
{
Text = string.Format("Test1 was selected but the selected item is {0}", selectedItem==null?"null":selectedItem.Name);
}
OnPropertyChanged("SelectedItem");
}
}
public MainViewModel()
{
List = new ObservableCollection<Item>()
{
new Item("Test1","Val1"),new Item("Test2","Val2"),new Item("Test3","Val3"),new Item("Test4","Val"),
};
OnPropertyChanged("List");
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (var propertyName in propertyNames)
{
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
}
}
public class Item : ViewModelBase
{
public string Name { get; set; }
public string Value { get; set; }
public Item(string name, string val)
{
Name = name;
Value = val;
OnPropertyChanged("Name");
}
}
note that when the Test1 Item selected the selected item didnot changed but in the view Test1 is marked as selected
At the point your MainViewModel.SelectedItem setter is called by the view, the view has already updated its selected item in the list. The binding simply informs the VM of this fact. The fact that you don't set MainViewModel.selectedItem means nothing to the view.
You would think that raising OnPropertyChanged("SelectedItem"); would force the view to re-evaluate its selected item, but in practice this does not work. I assume is down to some optimization within WPF or to prevent cyclic binding updates. (Remember you setter is already being called as part of a binding update, and you are trying to update the binding again)
If you wish to prevent something being selected in the view, then you need to disable it within the view, before it gets down to the VM. Here is one way of doing this.
I have a class with data:
public class routedata : INotifyPropertyChanged
{
private List<double> distances;
public List<double> Distances
{
get { return this.distances; }
set
{
if (this.distances != value)
{
this.distances = value;
this.onPropertyChanged("Distances");
}
}
}
private List<string> instructions;
public List<string> Instructions
{
get { return this.instructions; }
set
{
if (this.instructions != value)
{
this.instructions = value;
this.onPropertyChanged("Instructions");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void onPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
I'm trying to bind it to a listview like this:
<GridView Name="routeView" HorizontalAlignment="Left" Height="310" Margin="1025,318,0,0" Grid.Row="1"
VerticalAlignment="Top" Width="340" >
<ListView Name="routeList" Height="300" Width="330" ItemsSource="{Binding routeData}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Instructions}"
TextWrapping="Wrap" Width="200"/>
<TextBlock Text="{Binding Distances}"
Margin="10,0,0,0" />
<TextBlock Text="km"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GridView>
I have in my c# code behind: routeList.datacontext = this;
but it is still not binding, only one empty row is populated in the listview. I have checked the data and it is all present. Any help would be appreciated thank you.
A ListView takes a single collection as ItemsSource, so if you want to display multiple TextBlocks for each item - you need a collection of objects with multiple text properties to bind to your DataTemplate. In your case a routeData is not a collection. Instead you need to define your item view model, e.g.
public class RoutePoint
{
public double Distance { get; set; }
public string Instruction { get; set; }
}
then you would bind your ListView.ItemSource to a List and in your DataTemplate bind it like that:
<TextBlock Text="{Binding Distance}"/>
<TextBlock Text="{Binding Instruction}"/>
You don't need to use an ObservableCollection if your collection never changes after you bind it to the ListView for the first time (SelectedItem doesn't constitute a change).
If your view is called routeView, shouldn't your DataContext be set to a new instance of routedata? Also, I suggest you use an ObservableCollection<T> for your bindable collections rather than List<T>.
I am having trouble binding my View Model to my View. I am a beginner with MVVM, but I believe I am implementing my system (almost) correctly. I have a Model that contains data, which I am getting in my View Model, and then when my page is navigated to, I am attempting to grab that View Model data and binding it to the View.
My issue is that I have a ListBox in my View with 3 objects per item, and I cannot seem to bind to it correctly for each item in my list.
MainPage.xaml
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}"
SelectionChanged="FavoritesListBox_SelectionChanged">
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}"
Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress"
Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</ListBox>
MainPage.xaml.cs
public FavoritesPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
FavoritesListBox.DataContext = App.ViewModel;
}
App.xaml.cs
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
MainViewModel.cs
public ObservableCollection<ItemViewModel> FavoriteItems { get; private set; }
public MainViewModel()
{
//FavoriteItems = new ObservableCollection<ItemViewModel>();
FavoriteItems = Settings.FavoritesList.Value;
}
Settings.cs (The Model)
public static Setting<ObservableCollection<ItemViewModel>> FavoritesList =
new Setting<ObservableCollection<ItemViewModel>>(
"Favorites",
new ObservableCollection<ItemViewModel>());
ItemViewModel.cs
private string _favicon;
public string Favicon
{
get
{
return _favicon;
}
set
{
if (value != _favicon)
{
_favicon = value;
NotifyPropertyChanged("Favicon");
}
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _address;
public string Address
{
get
{
return _address;
}
set
{
if (value != _address)
{
_address = value;
NotifyPropertyChanged("Address");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
..and this is where and how I am saving each item (which should have three properties listed in the ItemViewModel
void addToFavorites_Click(object sender, EventArgs e)
{
var favoriteItem =
new ItemViewModel{
Favicon = "",
Name = "",
Address = TheBrowser.currentUrl() };
Settings.FavoritesList.Value.Add(favoriteItem);
}
Where FavoritesList is populated using an ItemViewModel containing 3 objects. The list is being populated correctly because during debugging I can see the entities in FavoritesList, but I am having an issue calling these entities in the view model to show up in my ListBox in the view?
I believe I am binding incorrectly but I'm not sure how to fix this?
In your XAML you bind to paths Name and Address do you have these 2 properties defined in your ItemViewModel?
Update after reading your code properly:
You are not updating the datatemplate of the Items of the Listbox. This is what you need to do:
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In addition to setting your DataContext to your viewmodel, (as mentioned in the comment linking to Creating ContextBinding XAML) , you also need to have your view model implement INotifyPropertyChanged (including ItemViewModel, which you don't show in your question)