I have a listview like:
<ListView Name="ListViewItems" Grid.Row="2" Foreground="White" ItemsSource="{Binding Items}"
SelectedItem="{Binding CurrentSelectedItem ,Mode=TwoWay}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Single"
HorizontalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto"
VerticalAlignment="Stretch" BorderThickness="0" >
<ListView.ItemTemplate>
<DataTemplate >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Width="0" Visibility="Collapsed" Content="{Binding Id}"/>
<Label Grid.Column="0" Content="{Binding Name}" HorizontalContentAlignment="Left" Foreground="White" FontWeight="Bold" Background="Transparent"/>
<Label Grid.Column="1" Content="{Binding Provider}" HorizontalContentAlignment="Left" Foreground="White" Background="Transparent"/>
<Label Grid.Column="0" Grid.ColumnSpan="2" Background="Transparent"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As it seems when i change the selected item (by clicking on another listview item) the set of CurrentSelectedItem will be called.
private MyObject _currentSelectedItem;
public MyObject CurrentSelectedItem
{
get => _currentSelectedItem;
set
{
if (Condition)
{
// here i want to have previous item seleced
// Currently i do nothing here
}
else
{
_currentSelectedItem = value;
}
}
}
When Condition is true if I do nothing the CurrentSelectedItem will have previous value which is what I want, BUT in the UI the selected item (highlighted item) is the one I selected recently.
What I want is to prevent such behavior in UI. I mean CurrentSelectedItem has not changed bu UI it changed.
Any solution to prevent such behavior? thanks in advance.
Solution:
As #Zeb-ur-Rehman we should save the previous state in _previousSelectedItem. but in the else statement i don't assign value to _currentSelectedItem as a result when I do _currentSelectedItem = _previousSelectedItem both current and previous mentioning the same object. and therefor OnPropertyChanged will not update the binded view.
By the way, the only way I could somehow manage this scenario is to save _currentSelectedMap in the previous and update UI by _currentSelectedMap = null.then after a small delay (it should be a delay) update UI by saved value like this:
if (Condition)
{
_previousSelectedItem = _currentSelectedItem;
_currentSelectedItem = null;
NotifyOfPropertyChange(()=> CurrentSelectedItem);
Task.Delay(100).ContinueWith(x =>
{
Application.Current.Dispatcher.Invoke(() =>
{
_currentSelectedItem = _previousSelectedItem;
NotifyOfPropertyChange(()=> CurrentSelectedItem);
});
});
}
else
{
_currentSelectedItem = value;
}
Here is what you need to do in your property setter.
private MyObject _previousSelectedItem;
private MyObject _currentSelectedItem;
public MyObject CurrentSelectedItem
{
get => _currentSelectedItem;
set
{
if (Condition)
{
_previousSelectedItem = _currentSelectedItem;
_currentSelectedItem = value;
}
else
{
_currentSelectedItem = _previousSelectedItem;
}
NotifyOfPropertyChange(()=> CurrentSelectedItem);
}
}
You need to keep track of previous value which you can capture when changing the currentSelectedItem. Next time if the condition is not true then just assign your currentSelecteditem the previous value.
Hope it will work for you.
Edit:
I haven't mentioned that you need to NotifyOfPropertyChange(()=> CurrentSelectedItem); becuase i thought it's too obvious. Your code should work this way as well. Insead of changing value in Dispatcher, as the value is already 2 way bind to control. This is the power of WPF binding.
Related
I used the property "UpdateSourceTrigger = PropertChanged". I am binding on a double. However, I can not do a '.' anymore, this is simply filtered out / deleted. How can I fix this an continue to use the property?
My Property:
private double textBoxZahl;
public double TextBoxZahl
{
get => textBoxZahl;
set
{
textBoxZahl = value;
OnPropertyChanged();
}
}
My TextBox:
<TextBox Margin="1,1,1,1" BorderThickness="1" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="0" Width="auto" x:Name="_NameBox" Background="WhiteSmoke" Text="{Binding ElementName=MaterialList, Path=SelectedItem.TextBoxZahl, UpdateSourceTrigger=PropertyChanged}"/>
My binding works fine, but not if I try to use a dot.
In my xaml I bind a ObservableCollection<City> Cities to it
and the SelectedItem is SelectedCity
and sometimes when the mouse is over a item, I cannot select it
My ListBox looks like this:
<ListBox ItemsSource="{Binding Model.Cities}" SelectedItem="{Binding Model.SelectedCity}" HorizontalAlignment="Left" Height="468" Margin="10,136,0,0" VerticalAlignment="Top" Width="877">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Text="{Binding Plz}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in My Model the code looks like this:
class CitiesModel: MyObservableCollection<City>
{
public ObservableCollection<City> Cities
{
get
{
return _cities;
}
}
private ObservableCollection<City> _cities;
private City _selectedCity;
public City SelectedCity
{
get
{
return _selectedCity;
}
set
{
_selectedCity = value;
RaisePropertyChanged("SelectedCity");
}
}
Can someone explain, why i sometimes cannot select an item?
You mean once you select one, you cannot select a different one.
Your problem is that if you have in the setter.
Remove it.
if(_selectedCity != value)
{
City is an object.
If you compare an object to another then unless you have provided an Equals method, they will be equal if of the same type.
Hence, once you select a City then any other City is equal.
So i have a float array which i want to have as ItemSource in a ListBox.
Inside the ItemTemplate i have a progress bar, that should bind its Progress value to the given float value. Yet i can't ever see that the values are actually bound to the Progress property.
The xaml code (i don't know whether i'm wrong but i expected that there's a implicit cast from float to double):
<ListBox ItemsSource="{Binding CoreLoads, Mode=OneWay}" BorderThickness="0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type sys:Double}">
<StackPanel>
<ctrl:MetroProgressBar Orientation="Vertical" Progress="{Binding}" ExtenedBorderWidth="0.2" Width="30" Height="50" VerticalAlignment="Center"
HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="2" Background="White" Margin="5"/>
<TextBlock Margin="0,3,0,3" HorizontalAlignment="Center" Text="{Binding LastUpdateTime, StringFormat='{}{0:hh:mm:ss tt}', Mode=OneWay}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
xmlns:sys="clr-namespace:System;assembly=mscorlib"
and the property itself:
public double[] CoreLoads
{
get { return cpuManagement.ProcessorInfo.LoadPercentages; }
}
Note: The progress bar i'm using is a custom control and inherits from System.Windows.Controls.Control.
Problem seems to be that the values of the array are ok but when bound to a TextBlock the values are 0, so the progress of the progress bar is always 0. So, am i having a correct data template for a double array? Or should i change to another type of collection?
I guess you should create a property in a ViewModel(assuming you're using MVVM pattern) which will represent selected value of the ListBox:
private double selectedCoreLoad;
public Double SelectedCoreLoad
{
get
{
return selectedCoreLoad;
}
set
{
if (selectedCoreLoad != value)
{
selectedCoreLoad = value;
RaisePropertyChanged("SelectedCoreLoad");
}
}
}
Then, you should bind selected value of the ListBox to this property:
<ListBox ItemsSource="{Binding CoreLoads, Mode=OneWay}" SelectedValue="{Binding SelectedCoreLoad, Mode=TwoWay}" BorderThickness="0">
<ctrl:MetroProgressBar Orientation="Vertical" Progress="{Binding SelectedCoreLoad}" ExtenedBorderWidth="0.2" Width="30" Height="50" VerticalAlignment="Center"
HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="2" Background="White" Margin="5"/>
UPD
Use ObservableCollection instead:
private ObservableCollection<Double> coreLoads;
public ObservableCollection<Double> CoreLoads
{
get { return coreLoads; }
set
{
coreLoads = value;
RaisePropertyChanged("CoreLoads");
}
}
So found the answer: ListBox seems not to like arrays as ItemsSource. After changing the source to a double-list everything works.
public List<double> CoreLoads
{
get { return new List<double>(cpuManagement.ProcessorInfo.LoadPercentages); }
}
To make shorter, let's say I have a Datagrid with a combobox a TextBox, and another Combobox. I'd like to show or hide the text or the combobox according to the value selected with the first Combo
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cboThenConstOrCol" SelectedIndex="0" SelectedItem="{Binding Source={StaticResource VM}, Path=cboElseSelectedItem, Mode=TwoWay}">
<ComboBoxItem Content="None"/>
<ComboBoxItem Content="Const" />
<ComboBoxItem Content="Col"/>
</ComboBox>
<TextBox Grid.Column="1" Text="{Binding ElseConst}" Visibility="{Binding Source={StaticResource VM}, Path= IsVisibleElseConst}" IsTabStop="{Binding Source={StaticResource VM}, Path=isElseConstTabStop}"></TextBox>
<ComboBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource VM}, Path=Fields,Mode=OneWay}" Visibility="{Binding Source={StaticResource VM}, Path= IsVisibleElseCol}" DisplayMemberPath="FieldName" />
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
in my Viewmodel i'm taking care of showing or hiding and is working correctly, but the problem is when I make the textbox visible all textbox in all Rows become visible. What I'd like to to is just apply to the row where the combobox selecteditem has been changed. I hope i was clear, otherwise please let me know to add additional info. thanks
From what I can gather you've got one ViewModel that controls everything. I think you'll run into problems doing this, and while it may seem easier doing things like this at first it definitely isn't when any amount of complexity is introduced. What I'd do is embrace MVVM a bit more and make a ViewModel for the item that each of your Rows represent. This allows each row to maintain it's own state. Here's an example based on what you've provided:
Modified XAML (incomplete):
<UserControl.Resources>
<local:VM
x:Key="vm" />
</UserControl.Resources>
<Grid
DataContext="{StaticResource vm}"
x:Name="LayoutRoot"
Background="White">
<sdk:DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding Items}">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox
ItemsSource="{Binding Source={StaticResource vm}, Path=Options}"
SelectedItem="{Binding Selected, Mode=TwoWay}" />
<TextBox
Grid.Column="1"
Text="Else"
Visibility="{Binding TextVisible}" />
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
Items is collection containing 'ItemViewModel's. The major change here is that the bindings in the data template now map to properties of 'ItemViewModel'. The exception to this is the ComboBox's ItemSource (which contains your 'None', 'Const' and 'Col' values), which binds to the Main VM.
Main VM:
public class VM : INotifyPropertyChanged
{
private readonly ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
private readonly ObservableCollection<string> options = new ObservableCollection<string>();
public ObservableCollection<ItemViewModel> Items { get { return items; } }
public ObservableCollection<string> Options { get { return options; } }
public VM()
{
options.Add("None");
options.Add("Const");
options.Add("Col");
//Create some dummy items
for (int i = 0; i < 10; i++)
{
var item = new ItemViewModel();
item.Name = i.ToString();
item.Selected = options[0];
items.Add(item);
}
}
//INotifyPropertyChanged stuff
Item VM:
public class ItemViewModel : INotifyPropertyChanged
{
private string selected;
private Visibility textVisible;
public string Selected
{
get { return selected; }
set
{
if (!string.IsNullOrWhiteSpace(value))
{
switch (value.ToLower())
{
case "none":
TextVisible = Visibility.Collapsed;
break;
case "const":
TextVisible = Visibility.Visible;
break;
case "col":
TextVisible = Visibility.Visible;
break;
}
}
selected = value;
}
}
public Visibility TextVisible
{
get { return textVisible; }
set
{
textVisible = value;
RaisePropertyChanged("TextVisible");
}
}
//INotifyPropertyChanged stuff
}
You can see here that when the selected value is changed for this item, some logic is run to determine whether the text should be visible or not.
There's a few things wrong with this but hopefully it gets you on the right path.
I have listbox which binds data to DataTable and now I need to access specific List Item, let's say first 5th one, from source behind code:
<ListBox Grid.Column="1" ItemsSource="{Binding ElementName=_this, Path=AllMainCategoriesTable}" HorizontalAlignment="Center" BorderBrush="Transparent" Background="Transparent" x:Name="lbMainCategories">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton Grid.Column="0" Content="{Binding Path=main_category_name}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
<Label Grid.Column="1" Width="30" Background="Transparent" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Here is my foreach loop to access each listitem but I am enable to cast liSelectedMainCategory to ListItem...What am I doing wrong????
foreach (ListItem liSelectedMainCategory in lbMainCategores.Items)
{ }
You are using the Items property which is a collection of containers. Elements in that collection would not cast the way you want. Consider using something like this fragment...
ICollectionView icv = CollectionViewSource.GetDefaultView(listBox1.Items);
foreach (var v in icv)
{
var lbi = v as ListBoxItem;
if (lbi != null)
{
var s = lbi.Content; // what you are after is here...
}
}