I'm attempting to implement a value converter that will change the color of a button on a user control based on the custom "MyUserControlStatus" property of the user control "MyUserControl".
The code-behind looks like this:
Public Class MyUserControl
Public Property MyUserControlStatus As Integer = 0
End Class
Public Class StatusIndicatorConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim indicatorbrush As Brush = Brushes.Transparent
Dim status As Integer = CType(value, Integer)
Select Case status
Case 0 : indicatorbrush = Brushes.Red
Case 1: indicatorbrush = Brushes.Green
Case 2 : indicatorbrush = Brushes.Yellow
End Select
Return indicatorbrush
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
The XAML looks like this:
<UserControl x:Class="MyUserControl"
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:MainApplicationName"
xmlns:wpf="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
mc:Ignorable="d"
>
<UserControl.Resources>
<local:StatusIndicatorConverter x:Key="MyStatusIndicatorConverter"/>
</UserControl.Resources>
<Button x:Name="ButtonStatusIndicator"
Background="{Binding Path=MyUserControlStatus, Converter={StaticResource MyStatusIndicatorConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<Button.Resources>
<Style TargetType="Border">
<Setter Property="Height" Value="10"/>
<Setter Property="Width" Value="10"/>
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
</Button>
</UserControl>
I get an empty, non-colored status. The breakpoints within the Convert function don't fire. I'm not sure what I'm doing wrong here, as all the parts seem to be in place. It should have made the button indicator red (for value=0). The expectation is that, as the MyUserControlStatus changes, the color of the indicator button will change, too, as it is bound to MyUserControlStatus and that value is converted to a color.
A Binding in a UserControl's XAML to one of its own properties must explicitly set the source object of the Binding, e.g. by setting the RelativeSource property.
Setting Mode=TwoWay and UpdateSourceTrigger=PropertyChanged is however pointless. It has no effect at all in this Binding.
Background="{Binding Path=MyUserControlStatus,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource MyStatusIndicatorConverter}}"
Besides that, the source property must fire a change notification in order to have the binding target property updated.
It should be implemented as a dependency property:
Public Shared ReadOnly MyUserControlStatusProperty As DependencyProperty =
DependencyProperty.Register(
name:="MyUserControlStatus",
propertyType:=GetType(Integer),
ownerType:=GetType(MyUserControl))
Public Property MyUserControlStatus As Integer
Get
Return CType(GetValue(MyUserControlStatusProperty), Integer)
End Get
Set
SetValue(MyUserControlStatusProperty, Value)
End Set
Related
I'm trying to create a simple AudioPlayer control multiple reuse in a solution I'm working on. I have seen numerous example in various posts and blogs around the net and from those have created a small control with four buttons.
The xaml is defined thus:
<UserControl x:Class="AudioPlayer"
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"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="150">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,0,0,0" />
</Style>
</StackPanel.Resources>
<MediaElement Name="media" Source="{Binding Source}" LoadedBehavior="{Binding LoadedBehavior}"/>
<Button Width="24" Height="24" x:Name="Repeat" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_repeat.png" ToolTip="Repeat"/>
</Button>
<Button Width="24" Height="24" x:Name="Play" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_play.png" ToolTip="Play"/>
</Button>
<Button Width="24" Height="24" x:Name="Pause" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_pause.png" ToolTip="Pause"/>
</Button>
<Button Width="24" Height="24" x:Name="Stop" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_stop.png" ToolTip="Stop"/>
</Button>
</StackPanel>
With fairly simple code in the background;
Public Class AudioPlayer
Public Sub New()
InitializeComponent()
DataContext = New AudioPlayerViewModel With {.MediaElement = media, .Source = "bag1.mp3", .LoadedBehavior = MediaState.Manual, .CanCommandExecute = True}
End Sub
End Class
Public Class AudioPlayerViewModel
Inherits DependencyObject
Public Sub New()
Me.MediaCommand = New MediaElementCommand(Me)
End Sub
Public Property MediaElement() As MediaElement
Public Property Source() As String
Public Property LoadedBehavior() As MediaState
Public Property CanCommandExecute() As Boolean
Public Property MediaCommand() As ICommand
End Class
Public Class MediaElementCommand
Implements ICommand
Private vm As AudioPlayerViewModel
Public Sub New(ByVal vm As AudioPlayerViewModel)
Me.vm = vm
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return vm.CanCommandExecute
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
End RaiseEvent
End Event
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
Dim action As String = DirectCast(parameter, String)
Select Case action.ToLower()
Case "play"
vm.MediaElement.Position = TimeSpan.Zero
vm.MediaElement.Play()
Case "stop"
vm.MediaElement.Stop()
Case "pause"
vm.MediaElement.Pause()
Case "resume"
vm.MediaElement.Play()
Case Else
Throw New NotSupportedException(String.Format("Unknown media action {0}", action))
End Select
End Sub
End Class
My question quite simply is this. From the code you can see that at present the sound that is being played is hard coded. What I would like to know is wheteher it would be possible to create a dependency property for this control (I presume it would be of type string to represent a path to a sound file but I'm not sure) so that when the control is created in other controls or windows their viewmodels can pass a sound property to it (if that makes sense!).
If it is possible where should I create it in respect of the code snippets shown?
Many thanks
You could create a DP, but it would not work the way users would expect.
For example, if the user were to write
<local:AudioPlayer Media="{Binding SomeString}" />
Then WPF tries to set Media = DataContext.SomeString
But since you have hardcoded DataContext = New AudioPlayerViewModel in the constructor, then the binding will most likely fail because users will be expecting their inherited DataContext to be used by the UserControl, but the hardcoded DataContext will be used instead.
It is always my advice to never hardcode the DataContext property inside of a UserControl. It breaks the entire WPF design pattern of having separate layers for UI and Data.
Either build a UserControl specifically for use with a specific Model or ViewModel being used as the DataContext, such as this :
<!-- Draw anything of type AudioPlayerViewModel with control AudioPlayer -->
<!-- DataContext will automatically set to the AudioPlayerViewModel -->
<DataTemplate DataType="{x:Type local:AudioPlayerViewModel}}">
<local:AudioPlayer />
</DataTemplate>
Or build it with the expectation that the DataContext can be absolutely anything, and DependencyProperites will be used to give the control the data it needs :
<!-- DataContext property can be anything, as long as it as the property MyString -->
<local:AudioPlayer Media="{Binding MyString}" />
The easiest way to get your code to work would probably be
Create the ViewModel as a private property instead of assiging it to the UserControl.DataContext
Bind or set the DataContext of the top level child inside your UserControl to your private property (in your case, the StackPanel)
Adjust the binding for your MediaElement to read from a custom DependencyProperty instead of from StackPanel.DataContext
Something like this :
<UserControl x:Name="MyAudioPlayer" ...>
<StackPanel x:Name="AudioPlayerRoot">
...
<MediaElement Source="{Binding ElementName=MyAudioPlayer, Path=MediaDependecyProperty}" ... />
...
</StackPanel>
</UserControl>
Public Sub New()
InitializeComponent()
AudioPlayerRoot.DataContext = New AudioPlayerViewModel ...
End Sub
So Once again i came here to get some anwsers to problems i can't resolve them by myself, so my problem is im getting a xamlparse execption each time the button style is loaded, inside the style i have a < rectangle > that should get one color defined by the user from a query on my MVVM. So far there was no problems, the problem is trying to give that color value to the grandientstop inside the LinearGradientBrush of the rectangle. The xaml code im using:
<Rectangle x:Name="rectangle" Fill="{Binding Path=StrColor, Converter={StaticResource FadingBrushConverter}, RelativeSource={RelativeSource AncestorType={x:Type Rectangle}}}" HorizontalAlignment="Right" Height="70" Margin="0,0,0,0" RadiusY="5" RadiusX="5" VerticalAlignment="Bottom" Width="70">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="8"/>
</Rectangle.Effect>
</Rectangle>
The "StrColor" is a Color property.
On my MVVM i have this converter:
Public Class FadingBrushConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
' TODO: Do some type-checking
Dim brush = New LinearGradientBrush()
Dim color = DirectCast(value, Color)
Dim Bcolor As System.Windows.Media.Color
Bcolor.R = 0
Bcolor.G = 0
Bcolor.B = 0
brush.StartPoint = New Point(0.5, 0)
brush.EndPoint = New Point(0.5, 1.27)
brush.GradientStops.Add(New GradientStop(color, 0))
brush.GradientStops.Add(New GradientStop(Bcolor, 1))
Return brush
End Function
Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
I really dont know what am i doing wrong here, i have been looking on the internet but so far i had no luck in fixing this problem!
I also have used DynamicResource but with no success!
Thanks for any help!
I think the problem is with the binding. "Fill" property of the Rectangle is bound to the strColor property of its parent Rectangle (with the "RelativeSource FindAncestor" argument). I don't think it is realistic to assume that a rectangle is placed in another rectangle. Even if is is somehow the case, the built-in Rectangle element doesn't have a strColor property. If you want to get the color from the ViewModel, you should try something in the sort of:
{Binding Path=StrColor, Converter={StaticResource FadingBrushConverter}"
Of course, this is assuming that the DataContext is set correctly.
I know how to do this on Windows Form App, but I couldn't find anyway of doing so on a WPF App.
How would I present the user a blocking DialogBox with Yes/No option and get/process the response from the user?
Here's an example:
string sMessageBoxText = "Do you want to continue?";
string sCaption = "My Test Application";
MessageBoxButton btnMessageBox = MessageBoxButton.YesNoCancel;
MessageBoxImage icnMessageBox = MessageBoxImage.Warning;
MessageBoxResult rsltMessageBox = MessageBox.Show(sMessageBoxText, sCaption, btnMessageBox, icnMessageBox);
switch (rsltMessageBox)
{
case MessageBoxResult.Yes:
/* ... */
break;
case MessageBoxResult.No:
/* ... */
break;
case MessageBoxResult.Cancel:
/* ... */
break;
}
Please note that while Radu's answer works, you cannot apply WPF styles to the MessageBox.
I took a different approach to this problem.
I created a class to serve as a View Model for my message window and I created a style for how I wanted my window to appear. Later in code I instantiated a new Window, set it's DataContext to an instance of my View Model, and set the Window's Style property to the style I created for the window.
I know it sounds a bit overkill, and I'm not sure how other people go about solving this same issue... but my solution is quite flexible and I'm starting to really like it.
For example, here is Dialog View Model:
Public Class MyDialogViewModel
Public Event Closed()
Public Property Message As String
Public Property Cancel As MyNamespace.RelayCommand
Public Property Close As MyNamespace.RelayCommand
Public Property WasCancelled As Boolean
Public Sub New()
WasCancelled = True
Cancel = New MyNamespace.RelayCommand(AddressOf CancelClicked)
Close = New MyNamespace.RelayCommand(AddressOf CloseClicked)
End Sub
Private Sub CancelClicked()
RaiseEvent Closed()
End Sub
Private Sub CloseClicked()
WasCancelled = False
RaiseEvent Closed()
End Sub
End Class
Here is my style for a basic "message" window:
<Style x:Key="myMessageStyle" TargetType="{x:Type myNameSpace:CustomDialogWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ControlTemplate.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
</Style>
</ControlTemplate.Resources>
<Border >
<DockPanel Margin="10,10,0,10">
<TextBlock Text="{Binding Message}" Width="Auto" TextWrapping="WrapWithOverflow" DockPanel.Dock="Top"
Margin="10"
Foreground="{StaticResource MyMessageBoxForegroundColor}"/>
<DockPanel Margin="5,0,0,0" DockPanel.Dock="Bottom">
<Button Content="Ok" Command="{Binding Close}" ></Button>
<Button Content="Cancel" Command="{Binding Cancel}" HorizontalAlignment="Stretch"></Button>
</DockPanel>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My CustomDialogWindow is simply a window with nothing in it:
(XAML)
<Window x:Class="CustomDialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CustomDialogWindow"
SizeToContent="WidthAndHeight">
</Window>
And in the CustomDialogWindow I have the following code so that the window closes when the user clicks cancel or ok:
Public Class CustomDialogWindow
Private Sub CustomDialogWindow_DataContextChanged(ByVal sender As Object, ByVal e As System.Windows.DependencyPropertyChangedEventArgs) Handles Me.DataContextChanged
Dim dContext As MyDialogViewModel= TryCast(DataContext, MyDialogViewModel)
If dContext IsNot Nothing Then
AddHandler DirectCast(DataContext, MyDialogViewModel).CloseWindow, AddressOf CloseWindow
End If
End Sub
Private Sub CloseWindow()
Me.Close()
End Sub
End Class
Now when I need to use the window I just instantiate a new CustomDialogWindow, set it's DataContext to a new instance of the DialogViewModel class, and set it's style to the "myMessageStyle":
Dim cdw As New CustomDialogWindow
Dim dvm As New DialogViewModel
dvm.Message = "Hello World!"
cdw.DataContext = dvm
cdw.ShowDialog()
If dvm.WasCancelled = False Then
'....'
End If
The reason why I like this approach is because I inherit from the MyDialogViewModel and provide more properties so that, for instance, I can display a bunch of options for the user to choose from. I just supply custom styles for each type of window I want to display (making sure to bind the appropriate properties). Like I said, it's very flexible and pretty simple to implement.
Cheers!
-Frinny
For some reason I can't hide WPF Toolkit's DataGridColumn. I am trying to do the following:
<dg:DataGridTemplateColumn Header="Item Description" Visibility="{Binding IsReadOnly}">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=ItemDescription}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
This doesn't work, since it's looking for a IsReadOnly property on the ItemSource (not a property of the current class).
If add this as a property of the ItemSource class that implements INoifyPropertyChanged, it still doesn't hide the column. Is there a way around this? I want the column to hid when a button click changes IsReadOnly property.
Assume IsReadOnly returns a Visibility value and is a dependency property
I am completely stuck, I would really appreciate the help! Thanks a lot!
Posted in this question:
WPF DataGrid: Binding DataGridColumn visibility to ContextMenu MenuItems Ischeked (MVVM)
Fubzot is using the binding code similar to
Visibility='{Binding (FrameworkElement.DataContext).IsReadOnly,
RelativeSource={x:Static RelativeSource.Self}}'
You may also want to check out this:
Forwarding the DataGrid’s DataContext to its’ columns..
which is also linked in the above question.
Just for my information: Do you see any Binding errors in your Output window using your current code?
If you want to bind to the DataGridColumn's IsReadOnly property, just add a RelativeSource to the Binding (and a converter):
<BooleanToVisibilityConverter x:Key="boolToVis" />
...
<dg:DataGridTemplateColumn Header="Item Description" Visibility="{Binding IsReadOnly, RelativeSource={RelativeSource Self}, Converter={StaticResource boolToVis}}">
Also, it looks like this StackOverflow question might be related to your problem.
you need to use a converter
Public Class BooleanToVisibilityConverter
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
If targetType Is GetType(Visibility) Then
If CBool(value) = True Then
Return Visibility.Hidden
Else
Return Visibility.Visible
End If
Else
Return Nothing
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
Return Nothing
End Function
End Class
then you use the converter in the XAML. SAMPLE
I am novice in WPF. I have a wpftoolkit datagrid where i am using a combo box as datagridcombox column. I am using a observable collection of Codes for binding the combo box. Below is the collection and its class...
#Region "Class & Coll"
Public Class CodesColl
Inherits ObservableCollection(Of Codes)
End Class
Public Class Codes
Private pCode As String
Private pDescription As String
Public Sub New()
pCode = String.Empty
pDescription = String.Empty
End Sub
#End Region
#Region "Property"
Public Property fldCode() As String
Get
Return pCode
End Get
Set(ByVal value As String)
pCode = value
End Set
End Property
Public Property fldDescription() As String
Get
Return pDescription
End Get
Set(ByVal value As String)
pDescription = value
End Set
End Property
#End Region
End Class
Now what i want achieve is that i need to bind the collection with dropdown in the grid.In my grid i have two columns in first column i have to display the code (fldCode) , and on the selection of the code the next column of the same row will get populated with its description (fldDescription).
My Xaml is something like this:
<wpfkit:DataGrid Margin="3" Style="{DynamicResource SimpleDataGrid}" FontWeight="Normal"
MaxHeight="100" ItemsSource="{Binding Source={StaticResource odpExistingCodesColl}}"
AutoGenerateColumns="False" Name="dgCodes" VerticalScrollBarVisibility="Visible" >
<wpfkit:DataGrid.Columns>
<wpfkit:DataGridTemplateColumn IsReadOnly="True">
<wpfkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Style="{DynamicResource SimpleImageDelete}"/>
</DataTemplate>
</wpfkit:DataGridTemplateColumn.CellTemplate>
</wpfkit:DataGridTemplateColumn>
<wpfkit:DataGridComboBoxColumn Header="Code"
DisplayMemberPath="fldCode"
SelectedValueBinding="{Binding fldCodes.fldCode}"
SelectedValuePath="fldCode"
SelectedItemBinding="{Binding fldCodeList}"
Width="100" x:Name="cbTCodes" >
<wpfkit:DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
<Setter Property ="ItemsSource" Value="{Binding Path=odpCodesColl}"/>
</Style>
</wpfkit:DataGridComboBoxColumn.ElementStyle>
<wpfkit:DataGridComboBoxColumn.EditingElementStyle >
<Style TargetType="ComboBox">
<Setter Property ="ItemsSource" Value="{Binding Path=odpCodesColl}"/>
<Setter Property ="IsDropDownOpen" Value="True"/>
</Style>
</wpfkit:DataGridComboBoxColumn.EditingElementStyle>
</wpfkit:DataGridComboBoxColumn>
<wpfkit:DataGridTextColumn Width="375" Header="Description" x:Name="tbTCodeDescription" />
</wpfkit:DataGrid.Columns>
</wpfkit:DataGrid>
odpExistingCodesColl here is another collection through which i am binding the entire grid and is used for sending the code and its description to but i am facing following problems
Unable to display the codes in dropdown.
Somehow i manged to do so but it disappears after loosing focus from the combobox.
Unable to retrive the description on its selection change as , i am unable to find the event too.
So you guys are requested to help me out asap.. any help will be highly appreciated..
Thanks in Advance
Amit Ranjan
You can check on Vincent's blog for detail information about how to work with Wpf DataGrid(DataGridComboBoxColumn too).