How can I dynamically get tooltip using converter? - wpf

If I run my project, my tooltip converter is run once - I need it to run each time the mouse hoover over a row.
Here's my XAML:
<Window x:Class="MainWindow"
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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MyTooltipConverter x:Key="MyTooltipConverter" />
</Window.Resources>
<Grid>
<DataGrid x:Name="dataGrid" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="263" Width="507">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content="{Binding ??, Converter={StaticResource MyTooltipConverter}}" />
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
</Grid>
</Window>
And code...
Imports System.Globalization
Class MainWindow
Public Class Person
Public Property Name As String
End Class
Public Persons As New List(Of Person)
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
For i As Integer = 0 To 5
Persons.Add(New Person With {.Name = "Test " + i.ToString})
Next
dataGrid.DataContext = Persons
End Sub
End Class
Public Class MyTooltipConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
If value Is Nothing Then
Return Nothing
End If
Dim panel As New StackPanel()
panel.Orientation = Orientation.Vertical
Dim block As New TextBlock()
block.Text = Now.ToString
panel.Children.Add(block)
Dim tip As New ToolTip()
tip.Content = panel
Return tip
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
How can I call MyTooltipConveter af get a Tooltip with the current time?
Thanks

Change your MyTooltipConverter.Convert method to Return panel. Returning tip throws a System.InvalidOperationException - 'ToolTip' cannot have a logical or visual parent.
As for the binding, just using the converter works.
<ToolTip Content="{Binding Converter={StaticResource MyTooltipConverter}}" />
Of course, for the time to update you have to move the mouse around to generate a new tooltip. If what you wanted was to have a tooltip that constantly updates then you need to add a timer and update the block.Text.
Something like this:
Public Class MyTooltipConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
If value Is Nothing Then
Return Nothing
End If
Dim panel = New StackPanel()
panel.Orientation = Orientation.Vertical
Dim block As New TextBlock()
block.Text = Now.ToString
panel.Children.Add(block)
Dim timer As New System.Windows.Threading.DispatcherTimer()
timer.Interval = New TimeSpan(0, 0, 1)
AddHandler timer.Tick, Sub()
block.Text = Now.ToString
End Sub
timer.Start()
Debug.WriteLine("Timer Started")
AddHandler panel.Unloaded, Sub(s, e)
timer.Stop()
timer = Nothing
panel = Nothing
Debug.WriteLine("Timer Stopped")
End Sub
Return panel
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class

Related

How to bind dates list datagridtextcolumn with Ivalueconverter

xaml
<Window x:Class="MainWindow"
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:ivaluecon"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:YesNoToBooleanConverter x:Key="YesNoToBooleanConverter" />
</Window.Resources>
<Grid>
<DataGrid x:Name="dg" Grid.Row="1" AutoGenerateColumns="False" HorizontalAlignment="Left" Height="163" Margin="25,61,0,0" VerticalAlignment="Top" Width="744">
<DataGridTextColumn Binding="{Binding d, Converter={StaticResource YesNoToBooleanConverter}}">
</DataGridTextColumn>
</DataGrid>
<DatePicker x:Name="sdate" HorizontalAlignment="Left" Height="33" Margin="66,312,0,0" VerticalAlignment="Top" Width="148"/>
<DatePicker x:Name="edate" HorizontalAlignment="Left" Height="33" Margin="272,313,0,0" VerticalAlignment="Top" Width="148"/>
<Button x:Name="button1" Click="button1_Click" Content="Button" HorizontalAlignment="Left" Height="41" Margin="452,308,0,0" VerticalAlignment="Top" Width="155"/>
</Grid>
I am not able to understand how to bind datelist columns.
class
Public Class YesNoToBooleanConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If value.tstring() = "true" Then
Return "Yes"
Else
Return "No"
End If
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Return New Object()
End Function
Here is my code for generating dateslist columns
Class MainWindow
Private Sub button1_Click(sender As Object, e As RoutedEventArgs)
Dim daysList As DataTable = New DataTable()
Dim d As DateTime = New DateTime(2020, 12, 1)
While d <= New DateTime(2020, 12, 14)
Dim columnName As String = d.ToString("dd.MM")
daysList.Columns.Add(columnName, GetType(Boolean))
Dim dd As DataGridTextColumn = New DataGridTextColumn() With {
.Header = columnName,
.Binding = New Binding("[" & columnName & "]")
}
dg.Columns.Add(dd)
d = d.AddDays(1)
End While
Dim truth = String.Format("{0:on;0;OFF}", True.GetHashCode())
Dim unTruth = String.Format("{0:on;0;OFF}", False.GetHashCode())
For r As Integer = 0 To 1 - 1
Dim row = daysList.NewRow()
For c As Integer = 0 To daysList.Columns.Count - 1
row(c) = (c Mod (r + 1)) = 0
Next
daysList.Rows.Add(row)
Next
dg.ItemsSource = daysList.DefaultView
End Sub
End Class
Someone help me how to bind datelist column with xaml. Please have a look once and give me any example on this issue to fix this. thankyou....
below is the screenshot of dateslist and columns are having True and False I need to change as Yes and No.
enter image description here

How to bind Ivalueconverter to list of dates Headercolumns? [duplicate]

xaml
<Window x:Class="MainWindow"
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:ivaluecon"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:YesNoToBooleanConverter x:Key="YesNoToBooleanConverter" />
</Window.Resources>
<Grid>
<DataGrid x:Name="dg" Grid.Row="1" AutoGenerateColumns="False" HorizontalAlignment="Left" Height="163" Margin="25,61,0,0" VerticalAlignment="Top" Width="744">
<DataGridTextColumn Binding="{Binding d, Converter={StaticResource YesNoToBooleanConverter}}">
</DataGridTextColumn>
</DataGrid>
<DatePicker x:Name="sdate" HorizontalAlignment="Left" Height="33" Margin="66,312,0,0" VerticalAlignment="Top" Width="148"/>
<DatePicker x:Name="edate" HorizontalAlignment="Left" Height="33" Margin="272,313,0,0" VerticalAlignment="Top" Width="148"/>
<Button x:Name="button1" Click="button1_Click" Content="Button" HorizontalAlignment="Left" Height="41" Margin="452,308,0,0" VerticalAlignment="Top" Width="155"/>
</Grid>
I am not able to understand how to bind datelist columns.
class
Public Class YesNoToBooleanConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If value.tstring() = "true" Then
Return "Yes"
Else
Return "No"
End If
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Return New Object()
End Function
Here is my code for generating dateslist columns
Class MainWindow
Private Sub button1_Click(sender As Object, e As RoutedEventArgs)
Dim daysList As DataTable = New DataTable()
Dim d As DateTime = New DateTime(2020, 12, 1)
While d <= New DateTime(2020, 12, 14)
Dim columnName As String = d.ToString("dd.MM")
daysList.Columns.Add(columnName, GetType(Boolean))
Dim dd As DataGridTextColumn = New DataGridTextColumn() With {
.Header = columnName,
.Binding = New Binding("[" & columnName & "]")
}
dg.Columns.Add(dd)
d = d.AddDays(1)
End While
Dim truth = String.Format("{0:on;0;OFF}", True.GetHashCode())
Dim unTruth = String.Format("{0:on;0;OFF}", False.GetHashCode())
For r As Integer = 0 To 1 - 1
Dim row = daysList.NewRow()
For c As Integer = 0 To daysList.Columns.Count - 1
row(c) = (c Mod (r + 1)) = 0
Next
daysList.Rows.Add(row)
Next
dg.ItemsSource = daysList.DefaultView
End Sub
End Class
Someone help me how to bind datelist column with xaml. Please have a look once and give me any example on this issue to fix this. thankyou....
below is the screenshot of dateslist and columns are having True and False I need to change as Yes and No.
enter image description here

WPF ComboBox Binding Selected Value

I am trying to do a binding as with following data formats:
Public Structure ItemBase
Property ID As String
Property Description As String
End Structure
Namespace Classes
Public Class StockEntityClass
Implements INotifyPropertyChanged
#Region "Property Variables"
Property ID As String
Property Namee As String
Property Units As String
Property ContactID As String
Property SetCount As Integer
Property VatOnMargin As Boolean
Property Vat As Double
Property Code As String
Property _ContactNamee As String
#End Region
Public Sub New()
_IDValue = Now.ToString
_NameeValue = ""
_UnitsValue = "Pcs"
_ContactIDValue = ""
_SetCountValue = 0
_VatOnMarginValue = False
_VatValue = 14.5
_CodeValue = ""
_ContactNamee = ""
End Sub
End Class
End Namespace
In my wpf xaml window which is DataContext to Stock Entity variable, I have a combo box that is bound to BindingList(of ItemBase) and the user will select an Item and that ItemID as to get assosiated to StockEntity.ContactID.
Following is the xaml code:
<ComboBox Name="VendorsComboBox" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" DisplayMemberPath="Description" SelectedItem="{Binding Path=CustomerID}"/>
Following is Binding Code:
Dim Stock As Classes.StockEntityClass
VendorsComboBox.ItemsSource = Contacts.DBAccessFunctions.Get_ContactsByType_BaseList(DataSource, "Vendor")
StockEntityStack.DataContext = Stock
Where StockEntityStack contains the UI part which is datacontext bind to stock variable.
Could you please tell me how to write the xaml databound to it.
Try something like that:
MainWindow (XAML file):
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox ItemsSource="{Binding MySource}"
SelectedValue="{Binding ContactID}"
DisplayMemberPath="Description"
SelectedValuePath="ID"
Width="180" Height="25"
/>
<StackPanel Orientation="Horizontal" Margin="10" >
<TextBlock Text="Selected ID:" />
<TextBlock Text="{Binding ContactID}" />
</StackPanel>
</StackPanel>
</Window>
MainWindow (code-behind file):
Class MainWindow
Public Sub New()
InitializeComponent()
Me.DataContext = New MainViewModel()
End Sub
End Class
MainViewModel file:
Imports System.ComponentModel
Public Class MainViewModel
Implements INotifyPropertyChanged
#Region "Fields"
Private _selectedContactID As String = String.Empty
#End Region
#Region "Property Variables"
Property MySource As List(Of ItemBase) = New List(Of ItemBase)
Public Property ContactID As String
Get
Return _selectedContactID
End Get
Set(ByVal value As String)
_selectedContactID = value
OnPropertyChanged("ContactID")
End Set
End Property
#End Region
Public Sub New()
MySource.Add(New ItemBase(1, "test1"))
MySource.Add(New ItemBase(2, "test2"))
MySource.Add(New ItemBase(3, "test3"))
MySource.Add(New ItemBase(4, "test4"))
MySource.Add(New ItemBase(5, "test5"))
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Structure ItemBase
Public Sub New( _
ByVal _id As String,
ByVal _description As String
)
ID = _id
Description = _description
End Sub
Property ID As String
Property Description As String
End Structure

Binding only part of the margin property of WPF control

I have this:
<TabControl Margin="0,24,0,0">...</TabControl>
I want to bind only the "Top" part of the TabControl, which intuitively I would do it this way:
<TabControl Margin="0,{Binding ElementName=TheMenu, Path=Height},0,0">
...
</TabControl>
How do I do it ?
Have you tried using a converter like this?
in VB.Net
Public Class MarginConverter
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
Return New Thickness(0, CDbl(value), 0, 0)
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 Nothing
End Function
End Class
Or in C#
public class MarginConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Thickness(0, System.Convert.ToDouble(value), 0, 0);
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
XAML
<Window.Resources>
<local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Slider Name="Slider1"></Slider>
<TabControl Name="TabControl" Margin="{Binding ElementName=Slider1, Path=Value, Converter={StaticResource marginConverter}}">
<Button>Some content</Button>
</TabControl>
</StackPanel>
</Grid>
Edit:
Using a MultiConverter
It is also possible to get all four values during run-time and use a MultiValueConverter. The Top-Property of the Thickness-Object is not a Dependency-Object, therefor you can't define a binding to it (unless your source is not a Dependency-Object).
XAML
<Window.Resources>
<local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
<local:MultiMarginConverter x:Key="multiMarginConverter"></local:MultiMarginConverter>
</Window.Resources>
<Grid>
<StackPanel>
<Slider Name="Slider1"></Slider>
<Slider Name="Slider2"></Slider>
<Slider Name="Slider3"></Slider>
<Slider Name="Slider4"></Slider>
<TabControl Name="TabControl">
<TabControl.Margin>
<MultiBinding Converter="{StaticResource multiMarginConverter}">
<Binding ElementName="Slider1" Path="Value"></Binding>
<Binding ElementName="Slider2" Path="Value"></Binding>
<Binding ElementName="Slider3" Path="Value"></Binding>
<Binding ElementName="Slider4" Path="Value"></Binding>
</MultiBinding>
</TabControl.Margin>
<Button>Some content</Button>
</TabControl>
</StackPanel>
</Grid>
... and c#
class MultiMarginConverter : IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Thickness(System.Convert.ToDouble(values[0]),
System.Convert.ToDouble(values[1]),
System.Convert.ToDouble(values[2]),
System.Convert.ToDouble(values[3]));
}
public object[] ConvertBack(object value, System.Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Edit(2) Reverse-Binding:
I'm not sure if this will make you happy. In my humble opinion I would try to avoid this, but ok... If your source is a Dependency-Property, you can bind this to the Margin:
<Slider Name="Slider5" Minimum="-99" Maximum="0" Value="{Binding ElementName=TabControl, Path=Margin.Top, Mode=OneWayToSource}"></Slider>
But I've got some effects with this.
The trick is, that you do not bind a part of the Margin of your TabControl to "something else", but bind "something else" to the Margin of your TabControl and specify Binding-Mode OneWayToSource.
Actually Margin property of a control is of Thickness Type. So we can bind it to Property if type Thickness.
public Thickness LeftMargin { get; set; }
and You can set a part of a Thickness object too. Like -
LeftMargin = new Thickness(20,0,0,0);
and in Xaml we can bind this property directly to margin property of any element..like this..
<TextBlock Text="Some Text" Margin="{Binding LeftMargin}" />
You could try something like this answer from another question.
The solution uses an attached property that allows for XAML like the following:
<Button ap:MoreProps.MarginRight="10" />
The attached property is also backed by a DependencyObject so data binding will work.
I have used this workaround for left margin only with StackPanel. Benefit is that you don't need any Converter.
<DockPanel VerticalAlignment="Top">
<TextBlock Name="tbkFulltextCaption"
Text="Static Caption:"
DockPanel.Dock="Left" />
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Bottom">
<FrameworkElement Name="feLeftMargin"
Width="{Binding Width, ElementName=tbkFulltextCaption, Mode=OneWay}" />
<TextBlock Text="(some text with margin of tbkFulltextCaption.Width)"
Name="tbkUnderNonsense"
FontSize="8"
Foreground="Gray">
</TextBlock>
</StackPanel>
<TextBox Name="tbFulltextSearch" />
</DockPanel>
preview
From your code, I figure that your menu and tabControl may overlap, so you want to use margin to separate them. I feel this practice like two Column CSS Layout.
Back to the point, I think you can apply TranslateFransform to TabControl.RenderTransform. You can bind Y property.
To expand on Ioop's method of making a property to control margin instead of a converter if you aren't attaching to another WPF element:
Create 4 standard properties and a readonly property, like so-
Public Class CustomMargin
Implements INotifyPropertyChanged
Private _Left As Double
Private _Right As Double
Private _Up As Double
Private _Down As Double
Public Sub New()
_Up = 0
_Down = 0
_Left = 0
_Right = 0
End Sub
Public Sub New(Vertical as Double, Horizontal as Double)
_Up = Vertical
_Down = Vertical
_Left = Horizontal
_Right = Horizontal
End Sub
Public Sub New(Left as Double, Up as Double, Right as Double, Down as Double)
_Up = Up
_Down = Down
_Left = Left
_Right = Right
End Sub
Public Property Left As Double
Get
Return _Left
End Get
Set(value As Double)
_Left = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Right As Double
Get
Return _Right
End Get
Set(value As Double)
_Right = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Up As Double
Get
Return _Up
End Get
Set(value As Double)
_Up = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public Property Down As Double
Get
Return _Down
End Get
Set(value As Double)
_Down = value
OnPropertyChanged(New PropertyChangedEventArgs("MyMargin"))
End Set
End Property
Public ReadOnly Property MyMargin As Thickness
Get
Return New Thickness(Left, Up, Right, Down)
End Get
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
If Not PropertyChangedEvent Is Nothing Then
RaiseEvent PropertyChanged(Me, e)
End If
End Sub
End Class
Then you just have to add the XAML-
<Label x:Name="MyLabel" Margin="{Binding Path=MyMargin, FallbackValue=0 0 0 0, Mode=OneWay}"/>
Then on the code behind on the WPF window-
Private _NewMargin as New CustomMargin
Public Sub New()
InitializeComponent()
MyLabel.DataContext = _NewMargin
End Sub
From there you can use whatever control you desire to change all 4 margins separately and the Class is reusable for other controls.
Ok it is old, but I was searching for a nicer way:
<TabControl>
<TabControl.Margin>
<Thickness Top="{Binding ElementName=TheMenu, Path=Height}" />
</TabControl.Margin>
</TabControl>

Can I use Generics to Simply Wpf Notifiable Bindings?

Can I use VB.Net's generics to simply Wpf notifiable bound controls?
Here's how to use VB.Net's Generics to create notifiable bound controls.
Class MainWindow
Public Sub New()
InitializeComponent()
Me.DataContext = Me
End Sub
Public Property NoNotify As String = "No notify"
Public Property DoesNotify As New NotifiableProperty(Of String)("DoesNotify")
Private Sub TestBtn_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles TestBtn.Click
NoNotify = "Won't Show"
DoesNotify.Value = "Notified!"
End Sub
End Class
Public Class NotifiableProperty(Of T)
Implements System.ComponentModel.INotifyPropertyChanged
Sub New()
End Sub
Sub New(ByVal InitialValue As T)
value = InitialValue
End Sub
Private m_Value As T
Public Property Value As T
Get
Return m_Value
End Get
Set(ByVal value As T)
m_Value = value
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Value"))
End Set
End Property
Private Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Button Name="TestBtn" Content="Click to Test" Width="72" />
<TextBox Name="NoNotifyTB" Text="{Binding NoNotify}" />
<TextBox Name="DoesNotifyTB" Text="{Binding DoesNotify.Value}"/>
</StackPanel>
</Grid>
</Window>

Resources