IMultiValueConverter + MultiBinding - Reverse Arithmatic Calculations on Data-Bound Values? - wpf

Apollogies for the weird title. I didnt know how else to put it.
What i have is 3 TextBoxes Data-Bound to some values.
What i need to do is have the value of 1 of the TextBoxes Automatically compute as a result of a calculation of the other 2 textboxes.
After some Google'ing, i found that using the IMultiValueConverter Interface should solve my problem. It does. But only 1 way.
For example:
TextBox 1 * TextBox 2 = TextBox 3
But the reverse is also true:
TextBox 3 / TextBox 2 = TextBox 1
The latter is what i'm having trouble with completing. No matter what i do, the reverse calculation wont stick.
I've Implemented 2 IMultiValueConverters, each for the 2 TextBoxes(since its 2 different calculations).
Converter 1:
Public Class SalaryConverter
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Dim salary As Double = 0
salary = (Math.Round(values(0) * (values(1) * 4)))
Return salary.ToString("C")
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Return Nothing
End Function
End Class
Converter 2:
Public Class RateConverter
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Dim rate As Double = 0
rate = ((values(0) / values(1)) / 4)
Return rate.ToString("C")
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Return Nothing
End Function
End Class
You'll see that the Return values for the ConvertBack methods is set to Nothing. This is because whenever i return the value parameter, i get an exception saying i cant convert from string to 1-dimensional array.
What am i doing wrong?
Any help is much appreciated.
Thanks in Advance.
P.S. I am fairly new to Converters. I only started playing around with it recently.

A multi value convert works as this :
the Convert method requires n input parameters and convert it into 1 result. In your case : TextBox 1 + TextBox 2 => TextBox 3
the ConvertBack is the reverse : it takes 1 input parameter and convert it into n results. In your case : TextBox 3 => TextBox 1 + TextBox 2
So your ConvertBack should return an array containing values of TextBox1 and TextBox2...
If you want to update only TextBox1 for exemple, you can do :
return new object[] { myTextBox1String, Binding.DoNothing };
EDIT : link to MSDN ConvertBack : http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.convertback.aspx

Related

How to inherit from an Object class in vb.net

I inherit from an Object class, my class clsMatrizDb to handle an Array(,); but when I do it generates the following
public objPubMatriz as clsMatrizDb(,)
objPubMatriz (0,0) = 1
I get an error saying that you can't convert from integer to clsXArrayDb
My class is inheriting like this
Public Class clsMatrizDb
Inherits Object
Private vIntRow As Integer
Private vIntColumn As Integer
Public Sub New()
MyBase.New
Me.vIntRow = 0
Me.vIntColumn = 0
End Sub
Public Sub New(ByVal pvIntRow As Integer, ByVal pvIntColumn As Integer)
Me.vIntRow = pvIntRow
Me.vIntColumn = pvIntColumn
End Sub
End Class
I'm missing something?
My comments ended up too long, so here's my explainer:
I agree with cleaning up comments made.
There is no need for Inherits Object. An instance of a class is an Object by default.
The default value of an Integer value is 0 so no need to set this explicitly in the New() sub. If you like, set it explicitly in the variable declarations.
There is no need to call MyBase.New because you are actually not inheriting from a base/abstract class.
You have stated that the error is converting from integer to clsXArrayDb but you don't mention this class at all. Do you mean clsMatrizDb?
The error you are getting is because you are trying to assign the integer value, 1 to an instance (object) of type clsMatrizDb.
If you want to create an instance of clsMatrizDb with Row and Column values initialised as 0, 0 then you just need to declare:
public objPubMatriz as New clsMatrizDb()
If you want to create an instance of clsMatrizDb with different Row and Column values (for example 2 and 4) then you would declare:
public objPubMatriz as New clsMatrizDb(2,4)
To be able to assign an integer value to your clsMatrizDb object, you will either need a default property as described by TnTinMn... which overrides a need for you to have a constructor that takes the Row and Column values. You would code:
public objPubMatriz as New clsMatrizDb()
objPubMatriz(2,4) = 1
The default property would be declared as:
Default Public Property Item(ByVal pvIntRow As Integer, ByVal pvIntColumn As Integer) As Integer
and you would set the values for vIntRow and vIntColumn as part of this property implementation.
If you want to keep the parameterised constructor then you will need to add a Value property to the class:
public property Value as Integer
With usage:
public objPubMatriz as New clsMatrizDb(2,4)
objPubMatriz.Value = 1

Colorpleth value converter

I need to display data on a WPF application colored in proportion to the measurement of the item's property value, like on the image below.
My question is how to provide the color/LinearGradientBrush (green-black-red) based on the relative value? Lowest values should return red(dish), middle values black/gray(ish) and highest values greed(ish), as in the image.
I started binding the background color of each panel to the item's value with a converter that can return some individual colors but I'd like to return a full range of possible LinearGradientBrushes based on the relative value.
Public Class ValueToColorConverter
Inherits MarkupExtension
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim v As Double = System.Convert.ToDecimal(value)
If v < 0 Then
Return Brushes.Red
ElseIf v < 0.05 Then
Return Brushes.Gray
Else
Return Brushes.Green
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New System.NotImplementedException()
End Function
Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
Return Me
End Function
End Class

How to check empty array

I am working in VB.net where i have class like below:
Public Class vertex
Public wasVisited As Boolean
Public name, type As String
Public x_pos, y_pos As Double
Public Sub New(ByVal x_pos As Double, ByVal y_pos As Double, ByVal name As Integer, ByVal type As String)
Me.x_pos = x_pos
Me.y_pos = y_pos
Me.name = name
Me.type = type
wasVisited = False
End Sub
End Class
I have object of some other class named as "graph" where in constructor of graph class I am calling constructor of vertex class.
I have array of vertex class: Public vertices() As vertex
And redim vertices(2000): resizing array again for some reason.
Now, when i loop the array to check empty value it throws an error:
Object reference not set to an instance of an object. (Since value contains "nothing")
even though i am checking like this,
If (vertices(i).name) Is Nothing Then
Exit For
End If
How can i check empty element of array?
Since you seem to want your collection be dynamic, a List(Of vertex) would serve you better. that along with a default New() constructor and you can add, remove, sort, search, whatever you need. To check for any empty value you can use If Vertices(i).name = "" then
Public Class vertex
Public wasVisited As Boolean
Public name, type As String
Public x_pos, y_pos As Double
Public Sub New()
wasVisited = False
name = ""
type = ""
x_pos = 0
y_pos = 0
End Sub
Public Sub New(ByVal x_pos As Double, ByVal y_pos As Double, ByVal name As String, ByVal type As String)
Me.x_pos = x_pos
Me.y_pos = y_pos
Me.name = name
Me.type = type
wasVisited = False
End Sub
End Class
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim Vertices As New List(Of vertex)
For I = 0 To 99
Vertices.Add(New vertex())
Vertices(I).name = "Test" + I.ToString
Next
End Sub
What's the size of vertices() before the redim operation ? If it's less than 2000, then the added elements will be Nothing right after the array enlargement, therefore when you try to access the name property of vertices(i) for values of i that go beyond the initial array size you're actually trying to dereference a null object reference.
You either need to check that vertices(i) IsNot Nothing before testing for the value of its properties or make sure every element of the array is assigned a new vertex object.
If vertices(i) Is Nothing OrElse vertices(i).name Is Nothing Then
Exit For
End If
Here's a thread on vbforums about a similar problem: http://www.vbforums.com/showthread.php?546668-RESOLVED-Redim-array-of-objects
Have you tried:
If Not vertices Is Nothing AndAlso Not vertices(i) Is Nothing _
AndAlso Not vertices(i).name Is Nothing Then
Dim value as string= vertices(i).name
End If

Wpf binding to a function

I've a created a simple scrollviewer (pnlDayScroller) and want to have a separate horizontal scrollbar (associated scroller) to do the horizontal scrolling. All works with the below code accept I need to bind the visibility of the associated scroller.
I can't simply bind this to the visibility property of the horizontal template part of the scroll viewer as I've set this to be always hidden. The only way I can think to do this is to bind the visibility of the associated scroller to a function such that
If associatedScroller.scrollableWidth > 0 then
associatedScroller.visibility = visibility.visible
else
associatedScroller.visibility = visibility.collapsed
end if
Is this possible to do and if so how do I do it?
Private Sub pnlDayScroller_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles pnlDayScroller.Loaded
Dim binViewport, binMax, binMin, binSChange, binLChange As Binding
Dim horizontalScrollBar As Primitives.ScrollBar = CType(pnlDayScroller.Template.FindName("PART_HorizontalScrollBar", pnlDayScroller), Primitives.ScrollBar)
binViewport = New Binding("ViewportSize")
binViewport.Mode = BindingMode.OneWay
binViewport.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.ViewportSizeProperty, binViewport)
binMax = New Binding("Maximum")
binMax.Mode = BindingMode.OneWay
binMax.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.MaximumProperty, binMax)
binMin = New Binding("Minimum")
binMin.Mode = BindingMode.OneWay
binMin.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.MinimumProperty, binMin)
binSChange = New Binding("SmallChange")
binSChange.Mode = BindingMode.OneWay
binSChange.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.SmallChangeProperty, binSChange)
binLChange = New Binding("LargeChange")
binLChange.Mode = BindingMode.OneWay
binLChange.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.LargeChangeProperty, binLChange)
End Sub
Private Sub associatedScroller_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) Handles associatedScroller.ValueChanged
pnlDayScroller.ScrollToHorizontalOffset(e.NewValue)
end sub
FOLLOW UP (thanks to JustABill) :
I've add this code into the pnlDayScroller sub above (I've discovered scrollableWidth is a property of scrollviewer not scrollbar, but the maximum property gives a result I can use instead)
binVisibility = New Binding("Maximum")
binVisibility.Mode = BindingMode.OneWay
binVisibility.Source = horizontalScrollBar
binVisibility.Converter = New ScrollableConverter
associatedScroller.SetBinding(Primitives.ScrollBar.VisibilityProperty, binVisibility)
and I've created this class
Public Class ScrollableConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim dblMaximum As Double
If targetType IsNot GetType(Visibility) Then
Throw New InvalidOperationException("The target must be a visibility")
Else
dblMaximum = CType(value, Double)
Debug.WriteLine("Value of double is " & dblMaximum)
If dblMaximum > 0 Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
And the problem is resolved.
You need a ValueConverter. Bind to the scrollableWidth property, and add your ValueConverter to the binding's Converter property. That example's in C#, but the concept's pretty simple, and I'm sure there's VB.Net examples around if you look.
The short form of what you need to do is:
Create a new class that implements IValueConverter (I think it's in System.ComponentModel).
Fill in the Convert method with your first code block, except use the "value" parameter instead of scrollableWidth and return the visibility.
Add an appropriate xmlns for your local classes.
Add a StaticResource of your new ValueConverter to your Window/UserControl/whatever.
Bind the Visibility property to the scrollableWidth property using this ValueConverter.

When binding with Wpf is there a way to use System.String funcntions without using converters?

When binding with Wpf is there a way to use System.String funcntions without using converters?
<TextBlock Text="({Binding Path=Text}).Trim()"/>
that's basically my desire.
I would use a converter.
Binding Xaml
<StackPanel>
<StackPanel.Resources>
<local:StringTrimmingConverter x:Key="trimmingConverter" />
<StackPanel.Resources>
<TextBlock Text="{Binding Path=Text, Converter={StaticResource trimmingConverter}}" />
</StackPanel>
StringTrimmingConverter.cs
using System;
using System.Windows.Data;
namespace WpfApplication1
{
[ValueConversion(typeof(string), typeof(string))]
public class StringTrimmingConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString().Trim();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
#endregion
}
}
And if VB StringTrimmingConverter.vb
Imports System.Globalization
Public Class StringTrimmingConverter
Implements IValueConverter
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return value.ToString().Trim
End Function
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
Return value
End Function
End Class
I created an ultimate converter for all the functions in System.String, needs some improvement would love to hear from you, hope to update it in future, please accept:
VB:
<ValueConversion(GetType(String), GetType(Object))> _
Class StringFunctions : Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse String.IsNullOrEmpty(parameter) Then Return Nothing
Dim parameters As New List(Of String)(parameter.ToString.Split(":"c))
parameter = parameters(0)
parameters.RemoveAt(0)
If String.IsNullOrEmpty(parameter) Then Return value
Dim method = (From m In GetType(String).GetMethods _
Where m.Name = parameter _
AndAlso m.GetParameters.Count = parameters.Count).FirstOrDefault
If method Is Nothing Then Return value
Return method.Invoke(value, parameters.ToArray)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Return value.ToString()
End Function
End Class
C#: -converted by a tool, don't rely!
[ValueConversion(typeof(string), typeof(object))]
public class StringConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
value = value.ToString();
if (String.IsNullOrEmpty(value as string)) return "";
if (parameter == null || !parameter is string || String.IsNullOrEmpty((string)parameter)) return value;
List<string> parameters = new List<string>(((string)parameter).Split(':'));
parameter = parameters[0];
parameters.RemoveAt(0);
var method = (from m in typeof(String).GetMethods()
where m.Name== parameter
&& m.GetParameters().Count()==parameters.Count
select m).FirstOrDefault();
if (method == null) return value;
return method.Invoke(value, parameters.ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
#endregion
}
Xaml:
<TextBox Text="{Binding Path=String, Converter={StaticResource StringConverter}, ConverterParameter=Trim:Argument:AnotherArgument}" />
Then, in the binding, when u use a converter u have an option to pass a parameter to the converter (Binding.ConverterParameter) pass all your parameters seperated with : (colon - you can change it in the String.Split delimiter parameter), while the first parameter is the function name, the function will count the extra parameters and try to pass it.
I still didn't work on the parameters addressing, it's a shallow function.
Would like to see your improvements and notes.
thanks.
Shimmy
You will need to use a converter as you want to transform the data your control is bound to. To avoid writing lots of converters simple transformations, you can use the Dynamic Language Runtime and write expressions in your favourite DLR scripting language (such as Python, Ruby, etc).
See my blog series for an example of how to achieve this. Part 3 talks specifically about ValueConverters.
I created an ultimate converter for all the functions in System.String, needs some improvement would love to hear from you, hope to update it in future, please accept:
<ValueConversion(GetType(String), GetType(String))> _
Class StringFunctions : Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse String.IsNullOrEmpty(parameter) Then Return Nothing
Dim parameters As New List(Of String)(parameter.ToString.Split(":"c))
parameter = parameters(0)
parameters.RemoveAt(0)
If String.IsNullOrEmpty(parameter) Then Return value
Dim method = (From m In GetType(String).GetMethods _
Where m.Name = parameter _
AndAlso m.GetParameters.Count = parameters.Count).FirstOrDefault
If method Is Nothing Then Return value
Return method.Invoke(value, parameters.ToArray)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotSupportedException
End Function
End Class
The in the binding, when u use a converter u have an option to pass a parameter to the converter (Binding.ConverterParameter) pass all your parameters seperated with : (colon - you can change it in the String.Split delimiter parameter), while the first parameter is the function name, the function will count the extra parameters and try to pass it.
I still didn't work on the parameters addressing, it's a shallow function.
Would like to see your improvements and notes.
thanks.
Shimmy
I know this post is old, but it is still the first one showing up when searching "WPF TextBox Binding Trim".
I don't have a VB answer, but please don't use a converter.
Reasons:
A converter means you have to add extra XAML code to all your XAML binding everytime. Having to always add extra code is just as bad in XAML as it in is C#/VB.
This will prevent you from typing a space if you have UpdateSourceTrigger=PropertyChanged set.
Use object oriented programming like it is supposed to be used. If you need a Trimmed TextBox, then create a child of TextBox called TrimmedTextBox and use that. http://www.wpfsharp.com/2014/05/15/a-simple-trimmedtextbox-for-wpf/
C#
using System.Windows.Controls;
namespace WpfSharp.UserControls
{
public class TrimmedTextBox : TextBox
{
public TrimmedTextBox()
{
LostFocus += TrimOnLostFocus;
}
void TrimOnLostFocus(object sender, System.Windows.RoutedEventArgs e)
{
var trimTextBox = sender as TrimmedTextBox;
if (trimTextBox != null)
trimTextBox.Text = trimTextBox.Text.Trim();
}
}
}
VB (I used a converter on my C# code)
Imports System.Windows.Controls
Namespace WpfSharp.UserControls
Public Class TrimmedTextBox
Inherits TextBox
Public Sub New()
AddHandler LostFocus, AddressOf TrimOnLostFocus
End Sub
Private Sub TrimOnLostFocus(sender As Object, e As System.Windows.RoutedEventArgs)
Dim trimTextBox = TryCast(sender, TrimmedTextBox)
If trimTextBox IsNot Nothing Then
trimTextBox.Text = trimTextBox.Text.Trim()
End If
End Sub
End Class
End Namespace
Hope this helps. Please feel free to use this object as if it were public domain.

Resources