WPF Binding does not update Button-Content - c#

May someone tell me whats wrong with this sourcecode?
when i click the button its not updating ist value?
At first binding the converter makes his job.
the sourcecode is pretty big so i will post only some snippets.
XAML:
Instances is type of ObservableCollection
<ListBox Name="Instances">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Tag="{Binding Path=Instance.Name}" Content="{Binding Path=Instance.Active, Converter={StaticResource BTSC}}" Click="ChangeAccess"/>
<TextBlock Text="{Binding Path=Instance.Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Converter:
public class BoolToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (((Boolean)value) == true)
return "No";
else
return "Yes";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Event:
private void ChangeAccess(object sender, RoutedEventArgs e)
{
for...
if ((sender as Button).Tag.ToString() == (DP.Instances[i].Instance as CInstance).Name)
{
SkipIfAndElse...
DP.Instances[i].Instance.Active = true;
}
}
CInstance:
class CInstance : INotifyPropertyChanged
{
private Boolean active;
public Boolean Active
{
get { return active; }
set
{
active = value;
NotifyPropertyChanged("Access");
}
}
}
All other values of the CInstance class are updating as expected.

In your CInstance class
NotifyPropertyChanged("Access");
should be
NotifyPropertyChanged("Active");

I would suggest you start using some kind of INPC framework. I personally like Simon Cropp's Fody.
Fody adds the appropriate OnNotifyPropertyChanged as a post compilation step, which means you don't get the Runtime hit that you get with Expression based solutions.
At the end of the day, string based OnNotifyPropertyChanged are all pretty fragile.

Related

Binding user control visibility to valueconverter

I do have a ValueConverter that works fine on elements e.g. like a STACKPANEL. The Method User.OwnsRight(Int32) returns true or false.
[ValueConversion(typeof(object), typeof(System.Windows.Visibility))]
public class ConverterUserRightVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bxSuite.Base.Objects.User locUser = (bxSuite.Base.Objects.User)value;
if (parameter == null) return System.Windows.Visibility.Visible;
if (locUser == null) return System.Windows.Visibility.Visible;
if (locUser.OwnsRight(System.Convert.ToInt32(parameter)))
{
return System.Windows.Visibility.Visible;
}
else
{
return System.Windows.Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
System.Windows.Visibility locVisibility = (System.Windows.Visibility)value;
if (locVisibility == System.Windows.Visibility.Hidden || locVisibility == System.Windows.Visibility.Collapsed)
{
return false;
}
else
{
return true;
}
}
}
So this XAML works great:
<StackPanel Visibility="{Binding Path=User, Converter={StaticResource ConverterUserRightVisibility}, ConverterParameter=4}"></StackPanel>
But if i do bind the Visibility of one of my UserControls to this Converter, the Converter doesn't even get fired. The UserControl itself works fine.
<my:MenuButtonLarge Visibility="{Binding Path=User, Converter={StaticResource ConverterUserRightVisibility}, ConverterParameter=4}"/>
Any ideas why? Any help appreciated! Thanks a lot.
EDIT: i've tried another Converter simply setting text on a Label control. Works fine, but if i use it on a Textproperty (dependency property) of a usercontrol the Converter with the ConverterParameter doesn't get fired? What am I missing?

Different Iitems in GridView

I'm trying to implement the following "start screen" interface for my Windows Store App.
I've figured a Gridview would be the component to use.
How do i display different type of items in a GridView?
Is this a good approach:
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<ContentControl Content="{Binding Converter={StaticResource local:ContentTypeToControlConverter}}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
And Class
public class ContentTypeToControlConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null)
{
if (value is MenuItem)
{
return new MenuItemControl();
}
else if (value is RecentViewItem)
{
return new RecentItemControl();
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
If you are targeting Windows 8.1 or higher - you could use the Hub control. That way you can avoid having to specify groups of items for your GridView, but implementing a DataTemplateSelector and setting it as a ItemTemplateSelector property of the GridView is the way to have items based on different templates.

How to pass a static value to IValueConverter in XAML

I would like to use static texts fetched from a web service in my WP7 app. Each text has a Name (the indetifier) and a Content property.
For example a text could look like this:
Name = "M43";
Content = "This is the text to be shown";
I would then like to pass the Name (i.e. the identifier) of the text to an IValueConverter, which would then look up the the Name and return the text.
I figured the converter to look something like this:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(value)).Content;
}
return null;
}
}
Then in the XAML:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="StaticTextConverter" />
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding 'M43', Converter={StaticResource StaticTextConverter}}"/>
However, this does not seem to work and I am not sure that I pass in the value to the converter correctly.
Does anyone have some suggestions?
I finally found the answer. The answer was a mix between that of #Shawn Kendrot and another question I asked here: IValueConverter not getting invoked in some scenarios
To summarize the solution for using the IValueConverter I have to bind my control in the following manor:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="TextConverter" />
</phone:PhoneApplicationPage.Resources>
<TextBlock Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
Since the ID of the text is passed in with the converter parameter, the converter looks almost the same:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null && parameter is string)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
However, as it turns out, the binding and thus the converter is not invoked if it does not have a DataContext. To solve this, the DataContext property of the control just has to be set to something arbitrary:
<TextBlock DataContext="arbitrary"
Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
And then everything works as intended!
The problem lies in your binding. It will check the DataContext, and on this object, it will try to evaluate the properties M62 and ValueboxConsent on that object.
You might want to add static keys somewhere in your application where you can bind to:
<TextBlock Text="{Binding Source="{x:Static M62.ValueboxConsent}", Converter={StaticResource StaticTextConverter}}" />
Where M62 is a static class where your keys are located.. like so:
public static class M62
{
public static string ValueboxConsent
{
get { return "myValueBoxConsentKey"; }
}
}
If you want to use a value converter, you'll need to pass the string to the parameter of value converter
Xaml:
<TextBlock Text="{Binding Converter={StaticResource StaticTextConverter}, ConverterParameter=M43}"/>
Converter:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
xmlns:prop="clr-namespace:MyProj.Properties;assembly=namespace:MyProj"
<TextBlock Text="{Binding Source={x:Static prop:Resources.MyString}, Converter={StaticResource StringToUpperCaseConverter}}" />

TextBox doesn't honor System Decimal (Dot or Comma)

If I bind Text in a TextBox to a float Property then the displayed text doesn't honor the system decimal (dot or comma). Instead it always displays a dot ('.'). But if I display the value in a MessageBox (using ToString()) then the correct System Decimal is used.
Xaml
<StackPanel>
<TextBox Name="floatTextBox"
Text="{Binding FloatValue}"
Width="75"
Height="23"
HorizontalAlignment="Left"/>
<Button Name="displayValueButton"
Content="Display value"
Width="75"
Height="23"
HorizontalAlignment="Left"
Click="displayValueButton_Click"/>
</StackPanel>
Code behind
public MainWindow()
{
InitializeComponent();
FloatValue = 1.234f;
this.DataContext = this;
}
public float FloatValue
{
get;
set;
}
private void displayValueButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(FloatValue.ToString());
}
As of now, I've solved this with a Converter that replaces dot with the System Decimal (which works) but what's the reason that this is neccessary? Is this by design and is there an easier way to solve this?
SystemDecimalConverter (in case someone else has the same problem)
public class SystemDecimalConverter : IValueConverter
{
private char m_systemDecimal = '#';
public SystemDecimalConverter()
{
m_systemDecimal = GetSystemDecimal();
}
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString().Replace('.', m_systemDecimal);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString().Replace(m_systemDecimal, '.');
}
public static char GetSystemDecimal()
{
return string.Format("{0}", 1.1f)[1];
}
}
Looks like there's a solution for this:
http://www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspx
Here is another discussion that can possibly help:
http://connect.microsoft.com/VisualStudio/feedback/details/442569/wpf-binding-uses-the-wrong-currentculture-by-default

C#/WPF: Make a GridViewColumn Visible=false?

Does anyone know if there is an option to hide a GridViewColumn somehow like this:
<ListView.View>
<GridView>
<GridViewColumn Header="Test" IsVisible="{Binding Path=ColumnIsVisible}" />
</GridView>
<ListView.View>
Thanks a lot!
Edit: For clarity
Unfortunately, there is no "IsVisible" Property. I'm looking for a way to create that.
Edit: The solution based on the feedback looks like:
<GridViewColumn DisplayMemberBinding="{Binding Path=OptionColumn1Text}"
Width="{Binding Path=SelectedEntitiy.OptionColumn1Width}">
<GridViewColumnHeader Content="{Binding Path=SelectedEntitiy.OptionColumn1Header}" IsEnabled="{Binding Path=SelectedEntitiy.OptionColumn1Width, Converter={StaticResource widthToBool}}" />
</GridViewColumn>
public class WidthToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value > 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Thanks to all!
Cheers
Edit: Reflecting the modified question.
What about creating a 0 width column? Write a Boolean to Width IValueConverter, that takes a ColumnIsVisible as the ConverterParmeter?
public class BooleanToWidthConverter : IValueConverter {
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture){
return ((bool) parameter)? value : 0;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture){
throw new NotImplementedException();
}
}
Something like:
<ListView .. >
<ListView.Resources>
<BooleanToWidthConverter x:Key="boolToWidth" />
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn
Header="Test"
Width=
"{Binding Path=ColumnWidth,
Converter={StaticResource boolToWidth},
ConverterParameter=ColumnIsVisible}" />
</GridView>
<ListView.View>
Here is another solution based on setting the column's width to zero. I have modified it a little. It now works like this:
Bind the header's visibility to a boolean property of the ViewModel, using a bool-to-visibility converter
Use an attached property on the header to set the column's width to zero
Here is the code.
XAML:
<GridViewColumn
HeaderTemplate="..."
HeaderContainerStyle="...">
<GridViewColumnHeader
Content="Header text"
Visibility="{Binding AppliesToColumnVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
behaviors:GridViewBehaviors.CollapseableColumn="True" />
BooleanToVisibilityConverter:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool param = bool.Parse(value.ToString());
if (param == true)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Attached behavior GridViewBehaviors.CollapseableColumn:
public static readonly DependencyProperty CollapseableColumnProperty =
DependencyProperty.RegisterAttached("CollapseableColumn", typeof(bool), typeof(GridViewBehaviors),
new UIPropertyMetadata(false, OnCollapseableColumnChanged));
public static bool GetCollapseableColumn(DependencyObject d)
{
return (bool)d.GetValue(CollapseableColumnProperty);
}
public static void SetCollapseableColumn(DependencyObject d, bool value)
{
d.SetValue(CollapseableColumnProperty, value);
}
private static void OnCollapseableColumnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
GridViewColumnHeader header = sender as GridViewColumnHeader;
if (header == null)
return;
header.IsVisibleChanged += new DependencyPropertyChangedEventHandler(AdjustWidth);
}
static void AdjustWidth(object sender, DependencyPropertyChangedEventArgs e)
{
GridViewColumnHeader header = sender as GridViewColumnHeader;
if (header == null)
return;
if (header.Visibility == Visibility.Collapsed)
header.Column.Width = 0;
else
header.Column.Width = double.NaN; // "Auto"
}
One simpler approach, that still uses the concept of setting the columns width to zero but does not have the side effects of using a IValueConverter (the user can still drag the column wider) is to create a new getter/setter that returns a width based on your ColumnIsVisible variable and then bind to that:
public double ColumnWidth
{
get
{
if (this.ColumnIsVisible)
{
return 100;
}
else
{
return 0;
}
}
set
{
OnPropertyChanged("ColumnWidth");
}
}
Make your bindings TwoWay and if the user attempts to drag the column wider OnPropertyChanged will be called and reset the width to 0. You might have to use a binding proxy though for your binding. Also add a call to OnPropertyChanged("ColumnWidth") when ever ColumnIsVisible is updated :)
I've set the column the width="0".
Now the column looks like its not visible. But i do not know if it will affect anything else.
It might be a dummy solution but for now it works.
Use if Thumb.DragDelta may solve the problem
I use it in listview as
<ListView x:Name="MyListView"IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Items}", Mode=Default, Source={StaticResource DataProvider}}"
Thumb.DragDelta="Thumb_DragDelta">
public Window1()
{
InitializeComponent();
MyListView.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(Thumb_DragDelta), true );
void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Thumb senderAsThumb = e.OriginalSource as Thumb;
GridViewColumnHeader header = senderAsThumb.TemplatedParent as GridViewColumnHeader;
if (header.Column.ActualWidth < MIN_WIDTH)
{
header.Column.Width = MIN_WIDTH;
}
if (header.Column.ActualWidth > MAX_WIDTH)
{
header.Column.Width = MAX_WIDTH;
}
}
}

Categories

Resources