WPF: Visibility binding, event = null - wpf

I have the following code:
public event EventHandler LoadingControlVisibilityChanged;
public Visibility LoadingControlVisibility
{
get { return _LoadingControlVisibility; }
set
{
_LoadingControlVisibility = value;
if (LoadingControlVisibilityChanged != null)
LoadingControlVisibilityChanged(this, EventArgs.Empty);
}
}
<Label x:Name="loading" Visibility="{Binding Path=LoadingControlVisibility, Mode=OneWay}" Content="No Devices Detected!" FontFamily="{DynamicResource AppFont}" HorizontalAlignment="Left" Margin="110,0,0,0" FontSize="21.333" />
The first time the binding work, but after I change the LoadingControlVisibility nothing happens, after debug I notice that the event = null. Please help me solve this problem.
my text property works with no problems:
public event EventHandler UUidChanged;
public string UUid
{
get { return _uuid; }
set
{
_uuid = value;
if (UUidChanged != null) UUidChanged(this, EventArgs.Empty);
}
}
<TextBox Text="{Binding Path=UUid, Mode=OneWay}" Margin="122.48,11.26,9,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="{DynamicResource MediumFontSize}" FontFamily="{DynamicResource AppFont}" Template="{DynamicResource TxtBoxTemplate}" Height="25" >
why is this different ?

The Binding statement will not look for the event you have defined. You must implement INotifyPropertyChanged instead.

I'm not quite certain what you're trying to accomplish. My understanding is that you are trying to bind the visibility of your label to a Property named LoadingControlVisibility that is defined in another class. If that is the case, then your path is wrong in the binding. Your binding should be as follows: Visibility="{Binding LoadingControlVisibility}"

Related

WPF make combobox binding twoways

I have a combo box that is editable, and i have a button that is enable when SelectedReplacement that binds to the combobox is not null and disable when it is. When it's null, i would input some random text to make the button enable, the problem is it wouldn't become enable when there I input text. making the Mode TwoWay doesn't help. i assumed setting the propertychangedevent would bind the new text to SelectedReplacement, but Im wrong, so any help is appreciated.
<ComboBox ItemsSource="{Binding SelectedError.Suggestions}"
Text="{m:Binding Path=SelectedError.SelectedReplacement, Mode=TwoWay}"
IsEditable="True"
HorizontalAlignment="Stretch"/>
i also tried to get the propertychanged
private void ViewModelPropertyChanged(SpellcheckViewModel sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(sender.SelectedError.SelectedReplacement))
{
_correctCommand?.Refresh();
}
}
I try to write a demo project to meet your requirement.
Mainly, the enable state is controlled by another boolean property IsButtonEnabled in the view model, the value for that property is controlled by InputText property which is controlled by the text you input in the ComboBox control.
Here is the UI:
<StackPanel Margin="10">
<ComboBox
x:Name="cmb"
IsEditable="True"
ItemsSource="{Binding AllItems}"
TextBoxBase.TextChanged="cmb_TextChanged"
TextSearch.TextPath="Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox
x:Name="hiddenTextBox"
Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="Collapsed" />
<Button
x:Name="btn"
Margin="0,10,0,0"
Content="Show message"
IsEnabled="{Binding IsButtonEnabled}" />
</StackPanel>
And here is the main logic in the view model:
public ObservableCollection<Item> AllItems
{
get { return _allItems; }
set { _allItems = value; this.RaisePropertyChanged("AllItems"); }
}
public bool IsButtonEnabled
{
get { return _isButtonEnabled; }
set { _isButtonEnabled = value; this.RaisePropertyChanged("IsButtonEnabled"); }
}
/// <summary>
/// When InputValue changed, change the enable state of the button based on the current conditions
/// </summary>
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
this.RaisePropertyChanged("InputText");
// You can control the enable state of the button easily
if (AllItems.Any(item => item.Name == value))
{
// SelectedItem is not null
IsButtonEnabled = true;
}
else if (!string.IsNullOrEmpty(value))
{
// SelectedItem is null
IsButtonEnabled = true;
}
else
{
IsButtonEnabled = false;
}
}
}
Finally, here is the project: ComboBoxDemo

Why isn't my user control with a combobox binding correctly?

I've got a really simple UserControl I'm trying to create that contains a list of US states. I am trying to expose the selected state via a "SelectedState" property. However, I'm having trouble trying to get this binding to fire once it's hooked up in another UserControl / form.
The XAML for the user control looks like this:
<UserControl x:Class="Sample.Desktop.UserControls.StateDropdown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Sample.Desktop.UserControls"
mc:Ignorable="d"
Width="170" Height="28"
d:DesignHeight="28" d:DesignWidth="170">
<ComboBox x:Name="cboState"
ItemsSource="{Binding StateList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Abbreviation}"></Label>
<Label> - </Label>
<Label Content="{Binding Name}"></Label>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the code-behind, I have this code:
public static readonly DependencyProperty SelectedStateProperty = DependencyProperty.Register("SelectedState",
typeof(USState),
typeof(StateDropdown),
new UIPropertyMetadata(null,
new PropertyChangedCallback(OnSelectedStateChanged),
new CoerceValueCallback(OnCoerceSelectedState)));
private static object OnCoerceSelectedState(DependencyObject o, object value)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
return stateDropdown.OnCoerceSelectedState((USState)value);
else
return value;
}
private static void OnSelectedStateChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
stateDropdown.OnSelectedStateChanged((USState)e.OldValue, (USState)e.NewValue);
}
protected virtual USState OnCoerceSelectedState(USState value)
{
// TODO: Keep the proposed value within the desired range.
return value;
}
protected virtual void OnSelectedStateChanged(USState oldValue, USState newValue)
{
// TODO: Add your property changed side-effects. Descendants can override as well.
}
public USState SelectedState
{
// IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
get
{
return (USState)GetValue(SelectedStateProperty);
}
set
{
SetValue(SelectedStateProperty, value);
}
}
I wasn't able to get the SelectedValue bound property of SelectedState to fire, so I ended up hooking up the SelectionChanged event.
private void cboState_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems?.Count > 0)
{
SelectedState = (USState)e.AddedItems[0];
}
}
In my other user control, I have this in the XAML:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState}" ></uc:StateDropdown>
And the ViewModel (I'm using Caliburn Micro), I have this property:
protected USState _selectedState;
public USState SelectedState
{
get { return _selectedState; }
set
{
_selectedState = value;
NotifyOfPropertyChange(() => SelectedState);
}
}
The combo is populated as expected. However, SelectedState is never fired/updated when I change the selection.
I had also previously tried using SelectedItem instead of SelectedValue, with the same results.
I'm sure I'm missing something obvious, but I'm having trouble seeing where I went wrong.
EDIT: Here's what fixed the binding.
I removed the SelectionChanged event. Then I modified my "hosting page" usercontrol to set TwoWay binding:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState, Mode=TwoWay}" ></uc:StateDropdown>
As soon as I added that, SelectedState started being updated when I changed the ComboBox value.
The only things I see, is this line :
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
You don't need it, because of the SelectionChanged event. And it can cause the problem.
Also I would bind the SelectedState of the UserControl using a TwoWay binding.
Hope that will help you.

WPF Renamed Property Doesn't Propagate to XAML

I'm a little new to WPF so bear with me. I have a property that is bound in my XAML, but if I rename it (right click --> Refactor --> Rename) in the .cs file it doesn't propagate to the XAML. Am I doing something wrong?
EDIT
Here's my code behind:
private string _selectedItem = null;
public string SelectedItemName
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged();
}
}
}
Here's my XAML:
<TextBox HorizontalAlignment="Left" Height="23" Margin="307,287,0,0" TextWrapping="Wrap"
Text="{Binding SelectedItemName}" VerticalAlignment="Top" Width="120" Name="txtTest2" />
Your setter needs to be public. WPF binding system can't change the text when you type into TextBox. The refactoring is never meant to change XAML files. Download ReSharper if you want to have such functionality.

Bind Image in DataTemplate to System.Drawing.Image

I am attempting to bind Image.Source in a DataTemplate to a System.Drawing.Image as discussed here: using XAML to bind to a System.Drawing.Image into a System.Windows.Image control
<UserControl.Resources>
<media:ImageConverter x:Key="imageConverter" />
<DataTemplate DataType="{x:Type data:GameTile}" >
<StackPanel Orientation="Vertical" Margin="5" Background="Transparent">
<Viewbox>
<TextBlock FontWeight="Bold" Text="{Binding PointValue}" TextAlignment="Center" FontSize="14" />
</Viewbox>
<Image Margin="0,5,0,0" Source="{Binding Path=Image.Image, Converter={StaticResource imageConverter}}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<loop:ListBox x:Name="listBox1"
ItemsSource="{Binding Path=GameModel.Game.GameTiles}"
ItemContainerStyle="{StaticResource GameTileContainerStyle}" Orientation="Vertical" />
</Grid>
The GameTile object has an Image (not a system.drawing.image) property that points to a Picture object which has an Image property of type System.Drawing.Image.
I am binding the ItemsSource on the ListBox to a GameTiles Collection on a Game object.
Objects
public class Game
{
public XPCollection<GameTile> GameTiles
{
get { return GetCollection<GameTile>("GameTiles"); }
}
}
public class GameTiles
{
Picture fImage;
public Picture Image
{
get { return fImage; }
set { SetPropertyValue<Picture>("Image", ref fImage, value); }
}
}
public class Picture
{
private FileData fFile;
public FileData File
{
get { return fFile; }
set
{
SetPropertyValue("File", ref fFile, value);
if (string.IsNullOrEmpty(fName))
{
fName = (value == null ? string.Empty : value.FileName);
}
fImage = null;
}
}
Image fImage;
public System.Drawing.Image Image
{
get
{
if (fImage == null)
{
try
{
MemoryStream stream = new MemoryStream();
fFile.SaveToStream(stream);
stream.Position = 0;
fImage = Image.FromStream(stream);
}
catch
{
//TODO: log exception
}
}
return fImage;
}
//set { SetPropertyValue<Image>("Image", ref fImage, value); }
}
}
The images are not showing up in the ListBoxItems, but any other property that I bind to in the DataTemplate will show up. It may be worth noting that I am using Devexpress Xpo as an ORM. Also the classes represented above do implement INotifyPropertyChanged.
Any thoughts on what I may be missing?
EDIT: Forgot to mention that I have implemented a value converter as mentioned in the post that I linked to above. However, if I put a breakpoint in the converter method, it is never called.
EDIT: Added the fFile property to the code above.
I can set an Image.Source to the GameTile.Image.Image property through c#(by converting it to BitmapImage), and have it work as expected, but I'm not sure how to accomplish that with a DataTemplate through c#. I would prefer to set the binding in XAML, but would settle for a c# workaround with a DataTemplate (or something else that would work). I am pretty confident that the issue is not with the GameTile.Image property pulling image from the database because if I manually set the source on an Image.Source in c#, the image is there. It simply isn't working in the DataTemplate.
Edit: Determined the issue to be related to properties that are not directly on the DataType that I am binding to for example with and GameTile has a (int)PointValue property, a (Picture object)Image property, and a (Prize object)Prize property.
If I bind to
<TextBlock Text="{Binding PointValue}" />
it works as expected.
But if I bind to
<TextBlock Text="{Binding Prize.Name}" />
it does not work.
And If I bind to
<Image Margin="0,5,0,0" Source="{Binding Image.BitmapImage}" />
it fails also. The following graphic shows the error that is being thrown by the binding.
BindingExpression path error: 'Name' property not found on 'object'
''XPCollection' (Hash=...)'. BindingExpression:Path=Prize.Name;
DataItem=GameTile' (HashCode=...); target element is
'TextBlock'(Name=''); target property is 'Text' (type 'String')
Thanks,
Eric
Found the solution.
Had to change this:
<TextBlock Text="{Binding Prize.Name}" />
to this:
<TextBlock Text="{Binding Prize!.Name}" />
The only difference is the exclamation point(!).
This also worked for the image property.
<Image Margin="0,5,0,0" Source="{Binding Path=Image!.Image, Converter={StaticResource imageConverter}}" />

How to bind a variable with a textblock

I was wondering how I would be able to bind a text block to a variable within my C# class.
Basically I have a "cart" variable in my .cs file. Within that Cart class I have access to the different totals.
The following is what I have for binding, but it does not seem to bind to the variable...
<StackPanel
Width="Auto"
Height="Auto"
Grid.ColumnSpan="2"
Grid.Row="5"
HorizontalAlignment="Right">
<TextBlock
Name="Subtotal"
FontFamily="Resources/#Charlemagne Std"
FontSize="20"
Text="{Binding ElementName=cart, Path=SubTotal}">
</TextBlock>
<TextBlock
Name="Tax"
FontFamily="Resources/#Charlemagne Std"
FontSize="20"
Text="{Binding ElementName=cart, Path=Tax}">
</TextBlock>
<TextBlock
Name="Total"
FontFamily="Resources/#Charlemagne Std"
FontSize="20"
Text="{Binding ElementName=cart, Path=Total}">
</TextBlock>
</StackPanel>
What is the correct way of doing it? Thanks again for the help!
If you further want the TextBoxes to update automatically when your cart class changes, your class must implement the INotifyPropertyChanged interface:
class Cart : INotifyPropertyChanged
{
// property changed event
public event PropertyChangedEventHandler PropertyChanged;
private int _subTotal;
private int _total;
private int _tax;
private void OnPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public int SubTotal
{
get
{
return _subTotal;
}
set
{
_subTotal = value;
OnPropertyChanged("SubTotal");
}
}
public int Total
{
get
{
return _total;
}
set
{
_total = value;
OnPropertyChanged("Total");
}
}
public int Tax
{
get
{
return _tax;
}
set
{
_tax = value;
OnPropertyChanged("Tax");
}
}
}
ElementName in binding is used to reference other controls, not variables in code behind. To reference variables in code behind, you need to assign that variable to a Control's DataContext property.
Replace every occurrence of following line of code :
<TextBlock Name="Subtotal" FontFamily="Resources/#Charlemagne Std" FontSize="20" Text="{Binding ElementName=cart, Path=SubTotal}"></TextBlock>
with :
<TextBlock Name="Subtotal" FontFamily="Resources/#Charlemagne Std" FontSize="20" Text="{Binding Path=SubTotal}"></TextBlock>
And in your Window's constructor or Load event, write following code :
this.DataContext = cart;
Two solutions..
First solution:
Put the cart as DataSource in your code behind:
DataSource = cart;
And bind to it as follows:
{Binding Path=PropertyOfCart}
Second solution:
Bind to your root control with an ElementName binding, and get the cart through a property on this control:
Name your root/parent control where cart is a propery:
<UserControl .....snip..... x:Name="Root">
Bind to it like this:
{Binding ElementName=Root, Path=Cart.PropertyOfCart}
Please note that Cart must be a property of your UserControl, and not a field
You need to set your class as the data source for your form. See also this question.

Resources