How do i implement logic in get, set in dependency property WPF? - c#

i have five textbox controls in a user control, i want to add a dependency property in such a way
public string MyValue
{
get
{
return Textbox1.Text.Trim() + "." + Textbox2.Text.Trim() + "." + Textbox3.Text.Trim() + "." + Textbox4.Text.Trim() + "|" + Textbox5.Text.Trim();
}
set
{
Textbox1.Text = value.Split('|')[0];
Textbox2.Text = value.Split('|')[1];
Textbox3.Text = value.Split('|')[2];
Textbox4.Text = value.Split('|')[3];
Textbox5.Text = value.Split('|')[4];
}
}
But it is not working. how do i create a dependency property which can be binded directly to a single property. Any help would be grateful.

There is more than one solution:
expose the full value with the property and use an IValueConverter to extract the parts
create five properties, each exposing a part of the full value
Both are MVVM-compliant but the second one may be more transparent by avoiding too much plumbing but you may need more notification (INotifyPropertyChanged) calls.
EDIT: complete implementation
The UserControl:
XAML:
<UserControl x:Class="WpfApplication1.SplitterControl"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<local:SplitConverter x:Key="splitConverter"></local:SplitConverter>
</UserControl.Resources>
<StackPanel x:Name="root" DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=MyValue,Mode=TwoWay,Converter={StaticResource splitConverter}}">
<TextBox x:Name="Textbox1" Text="{Binding [0],NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SourceUpdated="TextBox_SourceUpdated"></TextBox>
<TextBox x:Name="Textbox2" Text="{Binding [1],NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SourceUpdated="TextBox_SourceUpdated"></TextBox>
<TextBox x:Name="Textbox3" Text="{Binding [2],NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SourceUpdated="TextBox_SourceUpdated"></TextBox>
<TextBox x:Name="Textbox4" Text="{Binding [3],NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SourceUpdated="TextBox_SourceUpdated"></TextBox>
<TextBox x:Name="Textbox5" Text="{Binding [4],NotifyOnSourceUpdated=True,UpdateSourceTrigger=PropertyChanged}" SourceUpdated="TextBox_SourceUpdated"></TextBox>
</StackPanel>
</UserControl>
Code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class SplitterControl : UserControl
{
public string MyValue
{
get { return (string)GetValue(MyValueProperty); }
set { SetValue(MyValueProperty, value); }
}
public static readonly DependencyProperty MyValueProperty = DependencyProperty.Register("MyValue", typeof(string), typeof(SplitterControl));
public SplitterControl()
{
InitializeComponent();
}
private void TextBox_SourceUpdated(object sender, DataTransferEventArgs e)
{
root.GetBindingExpression(DataContextProperty).UpdateSource();
}
}
}
The IValueConverter:
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfApplication1
{
public class SplitConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as string).Split('|');
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.Join("|", value as string[]);
}
}
}
And in the parent control, e.g. the MainWindow:
<TextBox x:Name="input" Text="First|Second|Third|Fourth|Fifth"></TextBox>
<local:SplitterControl MyValue="{Binding ElementName=input,Path=Text,Mode=TwoWay}"></local:SplitterControl>
Edit the "input" TextBox to change the full string value and edit each TextBox in the UserControl to change each part.
Very tricky but should do what you want.

Related

ComboBox object doesn't display initial value

A ComboBox object with a data binding, when I propram like below, the content doesn't display until a selection operation made.
XAML:
<Window x:Class="Recipe_GUI.ComboboxTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Recipe_GUI"
xmlns:src="clr-namespace:RecipeLib;assembly=RecipeLib"
mc:Ignorable="d"
Title="ComboboxTest" Height="450" Width="800">
<Window.Resources>
<src:DataType x:Key="DataType"/>
</Window.Resources>
<Grid>
<ComboBox HorizontalAlignment="Left" Margin="125,82,0,0" VerticalAlignment="Top" Width="120"
x:Name="C01"
ItemsSource="{StaticResource ResourceKey=DataType}"
SelectedValue="{Binding Path=DataType}"/>
<!--SelectedValue="{Binding Path=Text, ElementName=T01}"/>-->
<TextBox Text="{Binding ElementName=C01, Path=SelectedIndex}" Margin="125,141,309,249"/>
<TextBox Text="{Binding Path=DataType}" Margin="125,202,309,188" x:Name="T01"/>
</Grid>
</Window>
When I change the code to binding the ComboBox to TextBox "T01"("T01" binding to the same object), the ComboBox initial value displayed as expected. The code and presentation like below.
<ComboBox HorizontalAlignment="Left" Margin="125,82,0,0" VerticalAlignment="Top" Width="120"
x:Name="C01"
ItemsSource="{StaticResource ResourceKey=DataType}"
SelectedValue="{Binding Path=Text, ElementName=T01}"/>
<!--SelectedValue="{Binding Path=DataType}"/>-->
The other related code are like below.
XAML.CS:
using RecipeLib;
using System.Windows;
namespace Recipe_GUI
{
/// <summary>
/// Interaction logic for ComboboxTest.xaml
/// </summary>
public partial class ComboboxTest : Window
{
Param param = new Param();
public ComboboxTest()
{
InitializeComponent();
DataContext = param;
}
}
}
Class Param:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace RecipeLib
{
public class Param : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private Enum_DataType dataType;
public Enum_DataType DataType
{
get { return dataType; }
set
{
dataType = value;
OnPropertyChanged("DataType");
}
}
public Param()
{
DataType = Enum_DataType.Float;
}
}
}
Enum_DataType and DataType:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
namespace RecipeLib
{
public enum Enum_DataType : int
{
Float = 0,
Integer = 1,
Enumeration = 2,
Bitmask = 3,
Time = 4
}
public class DataType : ObservableCollection<string>
{
public DataType() : base()
{
foreach (var item in Enum.GetNames(typeof(Enum_DataType)))
{
Add(item);
}
}
}
}
Please help answer why the first method doesn't work for the initial value, and how to fix? Thank you in advance!
Old Code
<ComboBox HorizontalAlignment="Left" Margin="125,82,0,0"
VerticalAlignment="Top" Width="120"
x:Name="C01"
ItemsSource="{StaticResource ResourceKey=DataType}"
SelectedValue="{Binding Path=DataType}"/>
it's an assumption so if anyone can correct me then please do it.
I believe the ComboBox has a "Text" property and in this case, you are binding an enum so you need a converter to convert the value from enum to string. so I added a converter and it worked.
Additional: each ComboBoxItem has a "Content" property which is a string type so when you choose any of the items the ComboBox "Text" property is set automatically. that's why it works when you select any of the items.
the same logic applies to your code where you bound the text property of Textblock to ComboBox property "SelectedValue"
Updated Code
Here are some modifications I did to your existing code
Converter class
Option: I named it "EnumToStringConverter" but you can name this class whatever you want
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.GetNames(value.GetType()).ElementAt((int)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.Parse(targetType, value.ToString());
}
}
XAML
<Window.Resources>
<local:DataType x:Key="DataType"/>
<local:EnumToStringConverter x:Key="EnumConverter" />
</Window.Resources>
<ComboBox HorizontalAlignment="Left" Margin="125,82,0,0" VerticalAlignment="Top" Width="120"
x:Name="C01"
ItemsSource="{StaticResource DataType}"
SelectedValue="{Binding DataType, Converter={StaticResource EnumConverter}}" />

WPF: ValueConverter (IValueConverter) does not work

I have a Window class, where I have several TextBlock elements which should receive a Background color by a value of a Binding property. The first "Converter binding" works fine and does everything expected. Today I tried to implement another "Converter binding" with another Converter used for it, but it does not work:
(I left out the ConvertBack methods because they are not necessary here):
namespace InsightTool.Gui.Helper {
[ValueConversion(typeof(double), typeof(Brush))]
public class AverageExecutionTimeToColorConverter : IValueConverter {
public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
double val;
double.TryParse(value.ToString(), out val);
if (val >= 10000) {
return Brushes.Red;
} else if (val >= 5000) {
return Brushes.Orange;
} else {
return Brushes.Green;
}
}
}
[ValueConversion(typeof(int), typeof(Brush))]
public class ThreadsAvailableCountToColorConverter : IValueConverter {
public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
int val;
int.TryParse(value.ToString(), out val);
if (val < 100) {
return Brushes.Red;
} else if (val < 200) {
return Brushes.Orange;
} else if (val < 500) {
return Brushes.Yellow;
} else {
return Brushes.Green;
}
}
}
}
In the Window class I used both converts as following:
<Window ...
x:Name="Main"
xmlns:Base="clr-namespace:InsightTool.Gui.Helper">
<Window.Resources>
<Base:ThreadsAvailableCountToColorConverter x:Key="ThreadsAvailableCntConverter"/>
<Base:AverageExecutionTimeToColorConverter x:Key="AvgExecutionTimeConverter"/>
</Window.Resources>
<!-- This one works fine-->
<TextBlock Width="10" Height="10" VerticalAlignment="Center" Background="{Binding ExecutionTimeAverage, Converter={StaticResource AvgExecutionTimeConverter}, ElementName=UCExecutionTimes}"/>
<!-- This one does not work-->
<TextBlock Width="10" Height="10" VerticalAlignment="Center" Background="{Binding ThreadsAvailableCount, Converter={StaticResource ThreadsAvailableCntConverter}, ElementName=Main}"/>
</Window>
Declaration of DependencyProperties:
public partial class UserControlExecutionTimes : UserControl {
public static readonly DependencyProperty ExecutionTimeAverageProperty =
DependencyProperty.Register("ExecutionTimeAverage", typeof(double), typeof(MainWindow), new FrameworkPropertyMetadata(double));
public double ExecutionTimeAverage {
get { return (double)GetValue(ExecutionTimeAverageProperty); }
set { SetValue(ExecutionTimeAverageProperty, value); }
}
}
public partial class MainWindow : Window {
public static readonly DependencyProperty ThreadsAvailableCountProperty = DependencyProperty.Register("ThreadsAvailableCount", typeof(int),
typeof(MainWindow), new FrameworkPropertyMetadata(int));
public int ThreadsAvailableCount {
get { return (int)GetValue(ThreadsAvailableCountProperty); }
set { SetValue(ThreadsAvailableCountProperty, value); }
}
}
Both DependencyProperties are set correctly and their values are displayed in the GUI. What do I miss here?
EDIT:
I also tested the following:
<Window>
<!-- This one works fine-->
<TextBlock Width="10" Height="10" VerticalAlignment="Center" Background="{Binding ThreadsAvailableCount, Converter={StaticResource AvgExecutionTimeConverter}, ElementName=Main}"/>
<!-- This one does not work-->
<TextBlock Width="10" Height="10" VerticalAlignment="Center" Background="{Binding ThreadsAvailableCount, Converter={StaticResource ThreadsAvailableCntConverter}, ElementName=Main}"/>
</Window>
It seems that there is a problem for the Binding to consume the return value of the "new" converter, but I have no clue why.
EDIT2
I check the bindings with Snoop and the result was the following:
The background property of the working converter binding looks like this:
But the background property of the not working converter binding looks this:
Another proof that ThreadsAvailableCount is set correctly (Binding to a Textblock):
It more and more seems to be a mistake in displaying the return value of the ThreadsAvailableCountToColorConverter. That is because in Debug mode, it stops at a breakpoint in the Convert method of the ThreadsAvailableCountToColorConverter. It even reachesreturn in the Convert method successfully.
Ah! Finally solved this. I had the exact same problem. With a TextBlock, with an IValueConverter converting to a Brush.
The binding was working, no errors or output. The value was getting into the IValueConverter code, I could debug right through to the return statement then... nothing!
You've done everything right, but you've automatically imported the wrong Brushes. I do it all the time with WPF.
replace the using statement:
using System.Drawing
with:
using System.Windows.Media
WPF uses System.Windows.Media.Brushes, but it's very easy to import the almost identical System.Drawing.Brushes and not notice. It all looks fine, until WPF gets hold of it and can't actually use it. But it fails 'gracefully' by falling back on the default colour.
I think could be multiple issues look at the 'output window' for binding expression errors.
1) Ensure that the textbox are rendered in separate areas and are not
overlapping.
2) Use relative path to get to the control and use it property in the binding expression
Your convertor looks fine.
Following is my xaml
<Window x:Class="StackOverflowBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:stackOverflowBinding="clr-namespace:StackOverflowBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<stackOverflowBinding:ThreadsAvailableCountToColorConverter x:Key="ThreadsAvailableCntConverter"/>
<stackOverflowBinding:AverageExecutionTimeToColorConverter x:Key="AvgExecutionTimeConverter"/>
</Window.Resources>
<Grid>
<!--<DatePicker
x:Name="newtally"
Text="{Binding CustomerLastTally,Mode=TwoWay}"
Margin="0 0 0 0"
/>-->
<!-- This one works fine-->
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Width="30" Height="30" Text="Break"/>
<TextBlock Grid.Row="1" Grid.Column="0" Width="30" Height="30" VerticalAlignment="Center" Text="Break" Background="{Binding ExecutionTimeAverage, Converter={StaticResource AvgExecutionTimeConverter}, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
<!-- This one does not work-->
<TextBlock Grid.Row="2" Grid.Column="0" Width="30" Height="30" VerticalAlignment="Center" Text="Break" Background ="{Binding ThreadsAvailableCount, Converter={StaticResource ThreadsAvailableCntConverter}, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
Following is my code behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace StackOverflowBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Dependency Property
public static readonly DependencyProperty ExecutionTimeAverageProperty =
DependencyProperty.Register("ExecutionTimeAverage", typeof(DateTime),
typeof(MainWindow), new FrameworkPropertyMetadata(DateTime.Now));
// .NET Property wrapper
public DateTime ExecutionTimeAverage
{
get { return (DateTime)GetValue(ExecutionTimeAverageProperty); }
set { SetValue(ExecutionTimeAverageProperty, value); }
}
// Dependency Property
public static readonly DependencyProperty ThreadsAvailableCountProperty =
DependencyProperty.Register("ThreadsAvailableCount", typeof(int),
typeof(MainWindow), new FrameworkPropertyMetadata(40));
// .NET Property wrapper
public int ThreadsAvailableCount
{
get { return (int)GetValue(ThreadsAvailableCountProperty); }
set { SetValue(ThreadsAvailableCountProperty, value); }
}
}
}
Following is my convertor
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
namespace StackOverflowBinding
{
[ValueConversion(typeof(double), typeof(Brush))]
public class AverageExecutionTimeToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double val;
double.TryParse(value.ToString(), out val);
if (val >= 10000)
{
return Brushes.Red;
}
else if (val >= 5000)
{
return Brushes.Orange;
}
else
{
return Brushes.Green;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[ValueConversion(typeof(int), typeof(Brush))]
public class ThreadsAvailableCountToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int val;
int.TryParse(value.ToString(), out val);
if (val < 100)
{
return Brushes.Red;
}
else if (val < 200)
{
return Brushes.Orange;
}
else if (val < 500)
{
return Brushes.Yellow;
}
else
{
return Brushes.Green;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Unknown property 'Converter' for type 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' encountered while parsing a Markup Extension

I am binding to an ObservableCollection called ScaleFactor to a ComboBox. The value of the ObservableCollection are simply 1, 2, 4 and 8. I want to use an IValueConverter to change these values to x1, x2, x4 and x8.
My MainWindow.xaml
<Window x:Class="TimeLineCanvas.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:timeline="clr-namespace:TimeLineCanvas.UserControls"
xmlns:helper="clr-namespace:TimeLineCanvas.Helpers"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<helper:ZoomConverter x:Key="ZoomConverter" />
</Grid.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding SSS}" HorizontalAlignment="Left" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding, Converter={StaticResource ZoomConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
</Window>
And the code behind
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace TimeLineCanvas
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Constructors
public MainWindow()
{
InitializeComponent();
SSS = new ObservableCollection<int>();
SSS.Add(1);
SSS.Add(2);
this.DataContext = this;
}
#endregion
public ObservableCollection<int> SSS { get; set; }
}
}
And the converter
using System;
using System.Windows.Data;
namespace TimeLineCanvas.Helpers
{
public class ZoomConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "x" + value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I don't know why this is, I'm not using MarkupExtensions so I don't think this link helps. Can any one shed any light?
Do not use a comma after Binding. This way you call the empty constructor on the Binding object.
{Binding, Converter={StaticResource ZoomConverter}}
should be
{Binding Converter={StaticResource ZoomConverter}}

XAML doesn't see my IValueConverter class

I'm creating a simple WP8, but I'm having troubles hiding (changing the Visibility property) on a control.
In XAML I've added xmlns:local="clr-namespace:MyProjectName" (I've also tried with using).
The XAML is then structured as follows:
<phone:PhoneApplicationPage
x:Class="MyProjectName.Pages.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyProjectName"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True" Margin="0,4,0,-4" Background="#FFBD3F3F">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}" >
<Grid x:Name="ContentPanel" Grid.Row="1" >
<Grid.Resources>
<local:VisibilityFormatter x:Key="FormatConverter" />
</Grid.Resources>
<phone:LongListSelector Grid.Row="4">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Obj}"
Visibility="{Binding ObjVisibility,
Mode=OneWay,
Converter={StaticResource FormatConverter}}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
The problem is at the <local:...> line: The name "VisibilityFormatter" does not exist in the namespace "clr-namespace:MyProjectName".
The class is defined as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace MyProjectName
{
public class Formatter
{
public class VisibilityFormatter : IValueConverter
{
// Retrieve the format string and use it to format the value.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var visibility = parameter as bool?;
return visibility.HasValue && visibility.Value ? Visibility.Visible : Visibility.Collapsed;
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
}
The class ObjInfo is a simple public class with two properties:
namespace MyProjectName.Models
{
public class ObjInfo
{
public bool ObjVisibility { get; set; }
public string Obj { get; set; }
}
}
It's similar to this question, but no migrating is involved. I'm developing on WP8 from the get-go.
What am I trying to achieve? Well. I'm storing whether the control should be visible or not in that bool property. Since the XAML control's property only grokks the Visibility enum, but not bool, I need to convert it to that enum.
The VisibilityFormatter is an inner class of the Formatter class. You don't really need the Formatter class, just make the VisibilityFormatter a top class, and the XAML parser will find it.
Also, the general naming convention for converters is XXXConverter and not XXXFormatter, but that's no rule.
You don't have MyProjectName.VisibilityFormatter in your project, you have
MyProjectName.Formatter.VisibilityFormatter
You should remove Formatter class and leave only:
namespace MyProjectName
{
public class VisibilityFormatter : IValueConverter
{
// Retrieve the format string and use it to format the value.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var visibility = parameter as bool?;
return visibility.HasValue && visibility.Value ? Visibility.Visible : Visibility.Collapsed;
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Why isn't this binding working in MVVM?

I'm setting up a binding to a ViewModel object from a WPF View, but the binding doesn't seem to be firing. I haven't done much work in MVVM, so I thought I'd ask to see if there's a reason why the binding to the image's source property isn't firing when the page is loaded.
Here's the page's XAML:
<Page x:Class="DallasPrintManager.PrintPage"
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:viewmodel="clr-namespace:DallasPrintManager.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="900"
DataContext="{Binding Main, Source={StaticResource PrintPage}}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Image Source="{Binding ImageDisplay}" />
</ScrollViewer>
</Grid>
</Page>
And here's the ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Windows.Input;
using DallasPrintManager.Commands;
using System.Net;
using System.IO;
using System.Windows;
public class PrintPageViewModel : INotifyPropertyChanged
{
private BitmapImage _imageDisplay;
public PrintPageViewModel()
{
ImageDisplay = getImage();
}
private BitmapImage getImage()
{
try
{
WebClient wc = new WebClient();
byte[] imageData = wc.DownloadData("http://localhost/TestImage.tif");
MemoryStream ms = new MemoryStream();
ms.Write(imageData, 0, imageData.Length);
System.Windows.Media.Imaging.BitmapImage wpfImg = new System.Windows.Media.Imaging.BitmapImage();
wpfImg.BeginInit();
wpfImg.StreamSource = ms;
wpfImg.EndInit();
return wpfImg;
//return (Bitmap)Bitmap.FromStream(ms);
}
catch (WebException e)
{
MessageBox.Show("Error fetching document:\n\n" + e.Message);
return null;
}
catch (Exception e)
{
if (e.Source == "System.Drawing")
MessageBox.Show("Error reading document.");
Console.Out.Write(e);
return null;
}
}
public BitmapImage ImageDisplay
{
get
{
return _imageDisplay;
}
set
{
if (value != _imageDisplay)
{
_imageDisplay = value;
OnPropertyChanged("ImageDisplay");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
The viewmodel is instantiated in app.xaml, and bound to by the print page.
Look in your Debug Output window. Do you see any binding errors? They pretty much tell you what you need.
If the Debug Output Window is not shown you can enable it with CTRL + Alt + O.
For further information add xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" inside your XAML and diag:PresentationTraceSources.TraceLevel=High to your binding:
<Page x:Class="DallasPrintManager.PrintPage"
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:viewmodel="clr-namespace:DallasPrintManager.ViewModel"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
[...]
DataContext="{Binding Main, Source={StaticResource PrintPage}}">
and:
<Image Source="{Binding ImageDisplay, diag:PresentationTraceSources.TraceLevel=High}" />
You should also make sure your properties of the bindings implement INotifyPropertyChanged if the bindings make use of UpdateSourceTrigger=PropertyChanged. Otherwise they will not update.
You can even create a dummyConverter:
public class DebugDummyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debugger.Break();
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debugger.Break();
return value;
}
}
and add it inside your binding Converter={StaticResource DebugDummyConverter} to get information about your value of the binding, but this only works if the binding is correctly set otherwise you will not get any information.
See http://www.wpf-tutorial.com/data-binding/debugging/ for more details.

Categories