Point is no longer accepted when I use the property UpdateSourceTrigger - wpf

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.

Related

WPF, Prevent selected item change in condition

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.

Binding properties to DataTemplate

I'm using a DataTemplate which is inside a ResourceDictionary file.
<DataTemplate x:Key="AlertWarningMessage">
<Grid>
<Border Visibility="{Binding DataContext.Visibility}" Background="{StaticResource ResourceKey=AlertWarningMessageBackground}" HorizontalAlignment="Stretch" Height="30">
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="WARNING !" FontWeight="Bold" Foreground="{StaticResource ResourceKey=AlertWarningMessageForeground}" FontSize="13"/>
<TextBlock Text="{Binding DataContext.Message}" Foreground="{StaticResource ResourceKey=AlertWarningMessageForeground}" Margin="5,0,0,0"/>
</WrapPanel>
</Border>
</Grid>
</DataTemplate>
I merge this dictionnary in my UserControl, and i'm using this template like this :
<ContentControl ContentTemplate="{StaticResource AlertWarningMessage}" Grid.Row="2" Margin="0,2,0,0" DataContext="{Binding AlertSummary, UpdateSourceTrigger=PropertyChanged}" />
In my VM, i'm using a class which have 2 properties :
Public Class AlertInfos
Public Property Visibility As Visibility
Public Property Message As String
Public Sub New(p_visibility As Visibility, p_msg As String)
Me.Visibility = p_visibility
Me.Message = p_msg
End Sub
End Class
Property VM as my class :
Private _alertSummary As AlertInfos
Public Property AlertSummary() As AlertInfos
Get
Return _alertSummary
End Get
Set(ByVal value As AlertInfos)
_alertSummary = value
RaisePropertyChanged("AlertSummary")
End Set
End Property
Properties of this object are set to Collapsed and String.Empty
Next, I change the values of this object, like this :
Public Sub ShowAlert()
Me.AlertSummary.Message = "Test"
Me.AlertSummary.Visibility = Visibility.Visible
'Me.StartTimerAlert()
RaisePropertyChanged("AlertSummary")
End Sub
But it's not working. There are 2 problems :
At the begining, when the Visibility is set to Collapsed, the Border is visible.
When I change the Message property, it is not visually updated.
I think there is a problem with my Binding, but I don't know where. I tried differents things, but there is always these problems.
Furthermore, I had bind the property directly in a TextBlock below the ContentControl, and the Binding working find.
Do you have any idea ?
You should change your data template to this:
<DataTemplate x:Key="AlertWarningMessage">
<Grid>
<Border Visibility="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Visibility}" Background="AliceBlue" HorizontalAlignment="Stretch" Height="30">
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="WARNING !" FontWeight="Bold" Foreground="Red" FontSize="13"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=DataContext.Message}" Foreground="Red" Margin="5,0,0,0"/>
</WrapPanel>
</Border>
</Grid>
</DataTemplate>
And your AlertInfos to this (it's on C# so try to translate it to VB)
public class AlertInfos
{
private string message;
public string Message
{
get
{
return this.message;
}
set
{
if (this.message != value)
{
this.message = value;
}
}
}
private Visibility visibility;
public Visibility Visibility
{
get
{
return this.visibility;
}
set
{
if (this.visibility != value)
{
this.visibility = value;
}
}
}
}
It should work, at least it's working on my PC
I'm not familiar with VB but Message needs to RaisePropertyChanged
Visibilities are commonly bound too bools which also RaisePropertyChanged - these then use a BooleanToVisibilityConverter
Make sure your properties are public - have private backing variables and RaisePropertyChanged.
private bool _isSomethingVisibile;
public bool IsSomethingVisibile
{
get { return _isSomethingVisibile; }
set
{
_isSomethingVisibile = value;
RaisePropertyChanged();
}
}
You don't need to preface your bindings with "DataContext" that is implied.

WPF XAML ListBox bind to array

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); }
}

Trying to bind datatable with listbox...something wrong

Please help...what am I doing wrong here? Trying to bind listbox to datatable. After debugging, i see data rows in the table but some how it is not binding to listbox.
FYI. _this is the name of my current window...
<ListBox Grid.Column="1" ItemsSource="{Binding ElementName=_this, Path=MainCategoriesTable}" 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>
</ListBox>
Below is the property trying to bind with...
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set { _dtMainCategory = value; }
}
For XAML to set the data context tocode behind this is what works for me
DataContext="{Binding RelativeSource={RelativeSource Self}}"
in code behind
this.DataContext = this;
But I used _this like you have used it successfully.
Set Presentation.Trace = High in the all XAML binding. That is not the exact syntax but if you start with Presentation it should be obvious.
Why no binding on label.
main_category_name is a public property? I notice it is in lower case.
DataTable works like a dictionary, not like an object. It doesn't expose your columns as properties, but each DataRow exposes an indexer that can be used to get the cell value. Therefore, you need to use indexer syntax:
<RadioButton Grid.Column="0" Content="{Binding Path=[main_category_name]}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
UPDATE
Another thing that bothers me is that your MainCategoriesTable property doesn't notify about changes. If it's changed after all Bindings have been initialized, it won't work (while DependencyProperty will, because it always notifies about changes). To make it work, your context class must implement INotifyPropertyChanged interface and your property must look like this:
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set
{
if(value == _dtMainCategory)
{
return;
}
_dtMainCategory = value;
var h = this.PropertyChanged;
if(h != null)
{
h(this, new PropertyChangedEventArgs("MainCategoriesTable"));
}
}
}

Binding a Path in the class to an int property

I would like to bind a property in the source data to an int. Take for instance:
<ComboBox Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" ItemsSource="{Binding Makes}" SelectedItem="{Binding Path=Make_ID}" DisplayMemberPath="MakeDesc" />
From the ViewModel:
public short Make_ID { get { return Vehicle.Make_ID; } set { Vehicle.Make_ID = value; OnPropertyChanged("Make_ID"); } }
Makes is a class that has ID, MakeDesc, etc. My View Model is interested in the selected make but only it's ID. I know I could do this with IValueConverters but I'd rather not have to do that -- I think there is a way to do that on the binding, I just can't remember how.
The answer is to use SelectedValue and SelectedPath instead.
<ComboBox Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" ItemsSource="{Binding Makes}" SelectedValue="{Binding Path=Make_ID}" SelectedValuePath="ID" DisplayMemberPath="MakeDesc" />

Resources