WPF ListBox Scroll to the bottom - wpf

I am using ObservableCollection as an ItemSource for my listBox component:
But the behavior of the control is not proper as for me. The matter I have scroll down to ths first occurence in my collection, but not last.
The sample list is: 1,1,2,3,4,5,6,7,8,9,11,22,33,1
When you enetr last 1 you component scroll up to first 1 :). This not what I am wating for.
Please advise. Here a code of component:
public class MyListBox : ListBox
{
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (Items.Count > 0)
{
var item = Items[Items.Count - 1];
UpdateLayout();
ScrollIntoView(item);
UpdateLayout();
}
}
}

Sorry but it needs to be a class as a List or OC is going to really do a value comparison. So you need to make identical values unique. I test this out and it works.
<StackPanel Orientation="Vertical" >
<ListBox x:Name="lbStringList" ItemsSource="{Binding Path=UniqueStringList}" DisplayMemberPath="Str" Height="60" VerticalAlignment="Top"/>
<Button Click="Button_Click" Content="56" />
</StackPanel>
private List<UniqueString> uniqueStringList = new List<UniqueString>()
{
new UniqueString("zero",0),
new UniqueString("one",1),
new UniqueString("two",2),
new UniqueString("three",3),
new UniqueString("four",4),
new UniqueString("five",5),
new UniqueString("six",6),
new UniqueString("seven",7),
new UniqueString("zero",8)
};
public MainWindow()
{
InitializeComponent();
}
public List<string> StringList { get { return new List<string>() { "one", "two", "three", "four", "five", "one" }; } }
public List<UniqueString> UniqueStringList
{
get
{
return uniqueStringList;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(sender.GetHashCode());
lbStringList.ScrollIntoView(lbStringList.Items[8]);
}
public class UniqueString
{
private Int32 id;
public string Str { get; private set; }
public override bool Equals(object obj)
{
UniqueString item = (UniqueString)obj;
return item.id == id;
}
public override int GetHashCode()
{
return id;
}
public UniqueString(string str, Int32 _id) { Str = str; id = _id; }
}

Related

XAML, ListBox, ListBoxItems

I am trying to get data on mouse click events. In item.content I see the necessary data, but it does not get them. I need to get the values of this object ... but I don’t know how. I would be very grateful for your advice or assistance.
public partial class MainWindow : Window
{
class Room_List
{
public string Name { get; set; }
public string IP { get; set; }
public string Status { get; set; }
public string other { get; set; }
public Room_List() { }
public Room_List(string name, string ip, string status)
{
Name = name;
IP = ip;
Status = status;
}
}
ObservableCollection<Room_List> data_room_list = new ObservableCollection<Room_List>();
/*ObservableCollection<Conference_Room> data_conference_room = new ObservableCollection<Conference_Room>();*/
public MainWindow()
{
InitializeComponent();
//this.DataContext = this;
listbox_room_list.ItemsSource = data_room_list;
//входные тестовые данные
data_room_list.Add(new Room_List("Переговорная 101", "10.45.130.1", "fsdf"));
data_room_list.Add(new Room_List("Переговорная 102", "10.45.130.2", "fs12df"));
data_room_list.Add(new Room_List("Переговорная 103", "10.45.130.3", "fsf21df"));
data_room_list.Add(new Room_List("Переговорная 104", "10.45.130.4", "fsderf"));
data_room_list.Add(new Room_List("Переговорная 105", "10.45.130.5", "fsagsddf"));
}
private void PlaceholdersListBox_OnPreviewMouseDown2(object sender, MouseButtonEventArgs e)
{
var item = ItemsControl.ContainerFromElement(sender as ListBox, e.OriginalSource as DependencyObject) as ListBoxItem;
if (item != null)
{
// ListBox item clicked - do some cool things here
} }
private void listBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string str = listbox_room_list.SelectedValue.ToString();
} } }
it`s was easy...but not for me, I was prompted :)
Room_List room_List = (Room_List)Item.Content;

Is it possible to hide/show a property in ExtendedToolkit PropertyGrid control?

I have a PropertyGrid control, which has its properties defined in a class, like this:
[DisplayName("Display Company Logo")]
[PropertyOrder(5)]
public bool HasLogo { get; set; }
[DisplayName("Logo File Path")]
[PropertyOrder(6)]
[Browsable(true)]
[Editor(typeof(FilePickerEditor), typeof(FilePickerEditor))]
public string LogoFilePath { get; set; }
Is it possible to hide LogoFilePath property, depending on whether HasLogo is checked or not? Or, at least, make custom FilePickerEditor read-only.
I was able to do solve that using a behavior. PropertyGrid defined like this:
<toolkitExt:PropertyGrid Tag="DependentVisibility,HasLogo,LogoFilePath"
SelectedObject="{Binding PropertyGridSourceObjectUserPreferences, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Behaviors>
<b:PropertyGridBehavior/>
</i:Interaction.Behaviors>
</toolkitExt:PropertyGrid>
Behavior:
public class PropertyGridBehavior : Behavior<PropertyGrid>
{
string _tag;
List<Tuple<string, string, string>> _dependentVisibilityList;
PropertyGrid _propertyGrid;
protected override void OnAttached()
{
_propertyGrid = AssociatedObject as PropertyGrid;
_dependentVisibilityList = new List<Tuple<string, string, string>>();
if(_propertyGrid.Tag !=null)
{
_tag = _propertyGrid.Tag.ToString();
foreach(var v in _tag.Split(';'))
{
if (v.Split(',').Count() != 3) return;
_dependentVisibilityList.Add(new Tuple<string, string, string>(v.Split(',')[0], v.Split(',')[1], v.Split(',')[2]));
}
}
_propertyGrid.Loaded += _propertyGrid_Loaded;
_propertyGrid.PropertyValueChanged += PropertyGrid_PropertyValueChanged;
}
private void _propertyGrid_Loaded(object sender, RoutedEventArgs e)
{
foreach(var v in _propertyGrid.Properties as PropertyItemCollection)
{
PropertyItemDependencyVisibilitySet(v.PropertyName, v.Value);
}
}
private void PropertyGrid_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e)
{
if (e.NewValue.GetType().Equals(typeof(bool)))
{
PropertyItem originalSource = (PropertyItem)e.OriginalSource;
PropertyItemDependencyVisibilitySet(originalSource.PropertyName, (bool)e.NewValue);
}
}
private void PropertyItemDependencyVisibilitySet(string propertyName, object propertyValue)
{
try
{
Tuple<string, string, string> dependentVisibilityItem = _dependentVisibilityList.Where(x => x.Item1 == "DependentVisibility" && x.Item2 == propertyName).FirstOrDefault();
if (dependentVisibilityItem != null)
{
PropertyItemCollection propertyCollection = _propertyGrid.Properties as PropertyItemCollection;
PropertyItem propertyItemDestination = propertyCollection.Where(x => x.PropertyName == dependentVisibilityItem.Item3).FirstOrDefault();
if (propertyItemDestination != null) propertyItemDestination.Visibility = (bool) propertyValue ? Visibility.Visible : Visibility.Collapsed;
}
}
catch
{
}
}
}

Why is not ICollectionView refreshed?

I cant figureout why my ICollectionView is not refreshed. Can anyone explain what I'm doing wrong?
I've made a viewmodel like this:
class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Entity> m_entities = new ObservableCollection<Entity>();
public ICollectionView EntitiesView { get; private set; }
public ICollectionView HighCountView { get; private set; }
public ViewModel()
{
m_entities.Add(new Entity() { Id = 1, Name = "Erik", Description = "The first" });
m_entities.Add(new Entity() { Id = 2, Name = "Olle", Description = "The second" });
m_entities.Add(new Entity() { Id = 3, Name = "Kim", Description = "The last" });
EntitiesView = CollectionViewSource.GetDefaultView(m_entities);
EntitiesView.CurrentChanged += new EventHandler(EntitiesView_CurrentChanged);
HighCountView = new CollectionView(m_entities);
using (HighCountView.DeferRefresh())
{
HighCountView.Filter = e => ((Entity)e).Count > 3;
}
}
private void EntitiesView_CurrentChanged(object sender, EventArgs e)
{
Entity current = EntitiesView.CurrentItem as Entity;
if(current!=null)
{
current.Count++;
HighCountView.Refresh(); // Do I need this line?
OnPropertyChanged("HighCountView"); // or this?
}
}
...and in my window I use it as the datacontext, like this:
public partial class MainWindow : Window
{
private ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = vm;
}
}
...and I'm doing my bindings in the XAML-code like this:
<ListBox Grid.Column="0" x:Name="listView1" DisplayMemberPath="Name" ItemsSource="{Binding EntitiesView}" IsSynchronizedWithCurrentItem="True" />
<ListView Grid.Column="1" x:Name="listView2" DisplayMemberPath="Name" ItemsSource="{Binding HighCountView}" IsSynchronizedWithCurrentItem="True" />
The problem is that all three Entities is always shown in listView2 despite that I set the Filter-property. Why?
EDIT
To made the sample complete, here is the Entity-class.
class Entity : INotifyPropertyChanged
{
private int m_id;
public int Id
{
bla bla.....
}
private string m_name;
public string Name
{
bla bla.....
}
private string m_description;
public string Description
{
bla bla.....
}
private int m_count;
public int Count
{
get { return m_count; }
set
{
if (value != m_count)
{
m_count = value;
OnPropertyChanged("Count");
}
}
}
public void Update()
{
Description = "Updated: " + (++Count).ToString() + " times.";
}
At last I found what was wrong.
If I change the line:
HighCountView = new CollectionView(m_entities);
to this
HighCountView = new ListCollectionView(m_entities);
then it works a expected.
I can also remove this line
OnPropertyChanged("HighCountView"); // or this?
I hope this can help somebody!

Silverlight Combobox - Setting the SelectedItem MVVM

I have a ViewModel that sets the value for the "UserStructure" property. The problem is that the combobox wont bind to the value.
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("SelectedStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding SelectedStructure,Mode=TwoWay}'
Width="200"
Height="25">
in xaml UserStructure instead SelectedStructure.
In your XAML SelectedItem is bound to SelectedStructure instead of UserStructure what you want
UPDATE:
Your code doesn't work because you should set to SelectedItem object that has reference equality with some object in ItemsSource. In your ViewModel you set Structures as result of one service operation and UserStructure as result of the other. UserStructure and some object in Structures can be equals (object.Equals) but not reference equals (object.ReferenceEquals). ComboBox like other ItemsControls doesn't compare items by equality, it compares they by identity. So, to have right code you should select from Structures object that equals to UserStructure and set it as UserStructure:
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
...
Structure userStructure = Structures.FirstOrDefault(s=>s.Equals(_unitOccupierDetail.Structure));
UserStructure = userStructure;
}
In this case you should be sure that Structures comes before. You can look at Reactive Extensions Observable.ForkJoin() method to syncronize 2 async calls.
Try such changes
public class OwnerOccupierAccountViewModel : ViewModelBase
{
/// <summary>
/// Load combobox structures
/// </summary>
private readonly LoadOperation<Structure> _loadStructures;
private readonly LoadOperation<UnitOccupierDetail> _loadUnitOccupierDetails;
//public ICommand SaveAccountSettingsCommand { get; set; }
#region Properties
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return _structures; }
set
{
_structures = value;
RaisePropertyChanged("Structures");
}
}
private Structure _userStructure;
public Structure UserStructure
{
get { return _userStructure; }
set
{
_userStructure = value;
RaisePropertyChanged("UserStructure");
}
}
private UnitOccupierDetail _unitOccupierDetail;
public UnitOccupierDetail UnitOccupierDetail
{
get { return _unitOccupierDetail; }
set
{
_unitOccupierDetail = value;
RaisePropertyChanged("UnitOccupierDetail");
}
}
#endregion
public OwnerOccupierAccountViewModel()
{
// SaveAccountSettingsCommand = new DelegateCommand(SaveAccountSettings, CanSave);
UserAccountContext _userAccountContext;
if (!DesignerProperties.IsInDesignTool)
{
var loggedInUser = new Guid(WebContext.Current.User.UserID.ToString());
_userAccountContext = new UserAccountContext();
#region load structures
_loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
_loadStructures.Completed += _loadStructuresCompleted;
#endregion
#region load user data
_loadUnitOccupierDetails =
_userAccountContext.Load(
_userAccountContext.GetUnitOccupierDetailsQuery().Where(
u => u.UserIDFK == loggedInUser && u.StructureFK == 92));
_loadUnitOccupierDetails.Completed += _loadUnitOccupierDetails_Completed;
#endregion
}
}
void _loadUnitOccupierDetails_Completed(object sender, EventArgs e)
{
_unitOccupierDetail= new UnitOccupierDetail();
_unitOccupierDetail = _loadUnitOccupierDetails.Entities.First();
_userStructure = _unitOccupierDetail.Structure;
}
void _loadStructuresCompleted(object sender, EventArgs e)
{
var theseStructures = new ObservableCollection<Structure>(_loadStructures.Entities);
Structures = theseStructures;
}
//private void SaveAccountSettings(object param)
//{
//}
//private static bool CanSave(object param)
//{
// return true;
//}
}
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
DisplayMemberPath='StructureName'
SelectedValuePath='IDStructure'
SelectedItem='{Binding UserStructure,Mode=TwoWay}'
Width="200"
Height="25">
Updated:
Hmm, maybe try changes here:
<ComboBox x:Name="cboApartments"
ItemsSource='{Binding Structures, Mode=TwoWay}'
SelectedItem='{Binding UserStructure, Mode=TwoWay}'
Width="200"
Height="25">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StructureName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Binding In Binding with Templates(WPF)

I have a WPF UI Bound to a collection of AwesomeClass
Now, AwesomeClass has a collection of AwesomeParts objects.
How can I make my UI In such a way that (as an example)
for each AwesomeClass instance, there is a Tab on a tab panel
and then for each awesome part in that, there is an object on a listbox, on that tab.
Basically: Awesomes->Tabs
And Then : Awesome.Awesomeparts->Tabs.Listbox
Following is the code to do what you are looking for :
public partial class TabWindow : Window
{
public TabWindow()
{
InitializeComponent();
List<AwesomeClass> items = new List<AwesomeClass>();
for (int i = 0; i < 10; i++)
{
items.Add(new AwesomeClass());
}
AwesomeTabs.ItemsSource = items;
Loaded += new RoutedEventHandler(TabWindow_Loaded);
}
// Method 1
void TabWindow_Loaded(object sender, RoutedEventArgs e)
{
FindListBox(AwesomeTabs);
}
private void FindListBox(DependencyObject obj)
{
Int32 count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is ListBox)
{
(child as ListBox).SelectionChanged += new SelectionChangedEventHandler(ListBox_SelectionChanged);
}
else
{
FindListBox(child);
}
}
}
// Method 2
private void ListBox_Loaded(object sender, RoutedEventArgs e)
{
(sender as ListBox).SelectionChanged += new SelectionChangedEventHandler(ListBox_SelectionChanged);
}
void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
MessageBox.Show(e.AddedItems[0].ToString());
}
catch (Exception)
{ }
}
}
class AwesomeClass
{
static Int32 count = 0;
public Int32 Index { get; set; }
public List<AwesomePart> Parts { get; protected set; }
// Method 3 : Preferred
private AwesomePart _selectedPart;
public AwesomePart SelectedPart
{
get { return _selectedPart; }
set
{
OnSelectionChanged(_selectedPart, value);
_selectedPart = value;
}
}
private void OnSelectionChanged(AwesomePart oldValue, AwesomePart newValue)
{
if (newValue != null) MessageBox.Show(newValue.ToString());
}
public AwesomeClass()
{
Index = ++count;
Parts = new List<AwesomePart>();
for (int i = 0; i < 10; i++)
{
Parts.Add(new AwesomePart());
}
}
public override string ToString()
{
return "Tab #" + Index.ToString();
}
}
class AwesomePart
{
static Int32 count = 0;
public Int32 Index { get; set; }
public AwesomePart()
{
Index = ++count;
}
public override string ToString()
{
return "Part #" + Index.ToString();
}
}
XAML:
<Grid>
<TabControl Name="AwesomeTabs">
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Parts}" SelectedItem="{Binding SelectedPart}" Loaded="ListBox_Loaded"></ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
Bind a List<AwesomeClass> to a headered content control. Each "AwesomeClass" object will be set as the datacontext for each "tab" in the headered content control.
Within the content control that is on each "tab", bind the DataContext (AwesomeClass) property that accesses the List<AwesomePart> to your Listbox control.
Make sense?
Cheers.

Resources