i have created a custom user control which im using on my main xaml control:
<Controls:CustomControl Width="200" Height="20"
TotalCount="{Binding TotalRecordCount}" SuccessCount="{Binding ValidationCount}" ErrorCount="{Binding ValidationErrorCount}" Margin="0,5,0,0" HorizontalAlignment="Left">
</Controls:CustomControl>
I wanted to make the private variables of my custom usercontrol being ErrorCount,SuccessCount and total count(which are of type int32) Bindable so i can bind values to them. Right now when i try to bind it to my item source it gives the following error e exception message is "Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Int32'
Many thanks,
Michelle
You need to implement the Properties using DependencyProperty don't use private variables to hold these values. Here is an example:-
#region public int SuccessCount
public int SuccessCount
{
get { return (int)GetValue(SuccessCountProperty); }
set { SetValue(SuccessCountProperty, value); }
}
public static readonly DependencyProperty SuccessCountProperty =
DependencyProperty.Register(
"SuccessCount",
typeof(int),
typeof(CustomControl),
new PropertyMetadata(0, OnSuccessCountPropertyChanged));
private static void OnSuccessCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl source = d as CustomControl;
int value = (int)e.NewValue;
// Do stuff when new value is assigned.
}
#endregion public int SuccessCount
In order for a property to be "Bindable", meaning that you can set it using DataBinding, that property needs to be a Dependency Property. For more info on Dependency Properties, please check this MSDN article.
hope this helps :)
Related
Using VB.net & WPF
I've converted code available at Overlaying Controls in WPF with Adorners from C# to VB.Net
Original C# Code
/// <summary>
/// Overlays a control with the specified content
/// </summary>
/// <typeparam name="TOverlay">The type of content to create the overlay from</typeparam>
public class OverlayAdorner<TOverlay> : Adorner, IDisposable where TOverlay : UIElement, new()
{
private UIElement _adorningElement; private AdornerLayer _layer; /// <summary> /// Overlay the specified element /// </summary> /// <param name="elementToAdorn">The element to overlay</param> /// <returns></returns> public static IDisposable Overlay(UIElement elementToAdorn) { return Overlay(elementToAdorn, new TOverlay()); }
/// <summary>
/// Overlays the element with the specified instance of TOverlay
/// </summary>
/// <param name="elementToAdorn">Element to overlay</param>
/// <param name="adorningElement">The content of the overlay</param>
/// <returns></returns>
public static IDisposable Overlay(UIElement elementToAdorn, TOverlay adorningElement)
{
var adorner = new OverlayAdorner<TOverlay>(elementToAdorn, adorningElement);
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn);
adorner._layer.Add(adorner);
return adorner as IDisposable;
}
private OverlayAdorner(UIElement elementToAdorn, UIElement adorningElement)
: base(elementToAdorn)
{
this._adorningElement = adorningElement;
if (adorningElement != null)
{
AddVisualChild(adorningElement);
}
Focusable = true;
}
protected override int VisualChildrenCount
{
get { return _adorningElement == null ? 0 : 1; }
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_adorningElement != null)
{
Point adorningPoint = new Point(0, 0);
_adorningElement.Arrange(new Rect(adorningPoint, this.AdornedElement.DesiredSize));
}
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
if (index == 0 && _adorningElement != null)
{
return _adorningElement;
}
return base.GetVisualChild(index);
}
public void Dispose()
{
_layer.Remove(this);
}
}
VB.Net Code (Converted by Me)
Public Class OverlayAdorner(Of TOverlay As {UIElement, New})
Inherits Adorner
Implements IDisposable
Private _adorningElement As UIElement
Private _layer As AdornerLayer
Public Shared Function Overlay(elementToAdorn As UIElement, adorningElement As TOverlay) As IDisposable
Dim adorner = New OverlayAdorner(Of TOverlay)(elementToAdorn, adorningElement)
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn)
adorner._layer.Add(adorner)
Return TryCast(adorner, IDisposable)
End Function
Private Sub New(elementToAdorn As UIElement, adorningElement As UIElement)
MyBase.New(elementToAdorn)
Me._adorningElement = adorningElement
If adorningElement IsNot Nothing Then
AddVisualChild(adorningElement)
End If
Focusable = True
End Sub
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return If(_adorningElement Is Nothing, 0, 1)
End Get
End Property
Protected Overrides Function ArrangeOverride(finalSize As Size) As Size
If _adorningElement IsNot Nothing Then
Dim adorningPoint As New Point(0, 0)
_adorningElement.Arrange(New Rect(adorningPoint, Me.AdornedElement.DesiredSize))
End If
Return finalSize
End Function
Protected Overrides Function GetVisualChild(index As Integer) As Visual
If index = 0 AndAlso _adorningElement IsNot Nothing Then
Return _adorningElement
End If
Return MyBase.GetVisualChild(index)
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_layer.Remove(Me)
End Sub
End Class
Now I've created MainWindow & an User Control UserControl1 in my test project and trying code
Using OverlayAdorner(Of UserControl1).Overlay(G1)
'Err in First Line Itself
End Using
Error Argument not specified for parameter 'adorningElement' of 'Public Shared Function Overlay(elementToAdorn As System.Windows.UIElement, adorningElement As TOverlay) As System.IDisposable'.
What is Wrong Here
Below code block given on blog post you are referring to has incorrect usage shown.
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot))
{
// Do some stuff here while adorner is overlaid
}
Should be (in C#):
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot, this)) // Here I assume `this` is somehow `UserControl`'s object
{
// Do some stuff here while adorner is overlaid
}
In VB.NET
Using OverlayAdorner(Of UserControl).Overlay(G1, UserControl1)
' Do some stuff here while adorner is overlaid
End Using
Also note that: OverlayAdorner(Of UserControl1) should be OverlayAdorner(Of UserControl) because T here should be Type's name not name of object instance.
I hope that helps.
Some preaching
From this question of yours I get hint that you need to work yourself on language more. Give more time to VB.NET or C#.NET and use intellisense in Visual Studio to get yourself familiar with the class library and parameter types to be passed into methods you use. This will help you working in new class libraries which have some to no documentation.
I use this shortcut more when working in new class libaries. Type ctrl+k then ctrl+i. Generally said as ctrl+k,i
Update
Based on recent comment, Check the actual usage below:
XAML snippet:
<Window x:Class="WpfTestApp.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 x:Name="G1">
<UserControl x:Name="ucMyControl"></UserControl>
</Grid>
</Window>
Code behind snippet:
Using OverlayAdorner(Of UserControl).Overlay(G1, ucMyControl)
' Do some stuff here while adorner is overlaid
End Using
I have the following custom datagrid column, for passing in the value of the DatePart dependency property as the ConverterParameter to the editing element's converter:
Public Class DataGridTimeColumn
Inherits DataGridTextColumn
Shared ReadOnly DatePartProperty = DependencyProperty.Register("DatePart", GetType(DateTime?), GetType(DataGridTimeColumn), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf RefreshBinding))
Property DatePart As DateTime?
Get
Return GetValue(DatePartProperty)
End Get
Set(value As DateTime?)
SetValue(DatePartProperty, value)
End Set
End Property
Private Shared Sub RefreshBinding(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tc As DataGridTimeColumn = d
tc.Binding = tc.Binding
End Sub
Public Overrides Property Binding As BindingBase
Get
Return MyBase.Binding
End Get
Set(value As BindingBase)
Dim b As Data.Binding = value
With b
.Converter = New TimeConverter
.ConverterParameter = DatePart
End With
MyBase.Binding = b
End Set
End Property
End Class
With the following XAML:
<my:DataGridTimeColumn Header="From" Binding="{Binding FromDate}" DatePart="{Binding FromDate}" />
<my:DataGridTimeColumn Header="Until" Binding="{Binding TillDate}" DatePart="{Binding TillDate}" />
But RefreshBinding is never called (I've set a breakpoint and it's never triggered), and thus DatePart is always Nothing(null) when the ConverterParameter is set. How can I fix this?
Edit
In C#:
public class DataGridTimeColumn : DataGridTextColumn
{
static readonly DependencyProperty DatePartProperty = DependencyProperty.Register(
"DatePart", typeof(DateTime?), typeof(DataGridTimeColumn),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, RefreshBinding)
);
public DateTime? DatePart
{
get { return (DateTime?)GetValue(DatePartProperty); }
set { SetValue(DatePartProperty, value); }
}
private static void RefreshBinding(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tc = (DataGridTimeColumn)d;
tc.Binding = tc.Binding;
}
public override System.Windows.Data.BindingBase Binding
{
get { return base.Binding; }
set
{
var b = (Binding)value;
b.Converter = new TimeConverter();
b.ConverterParameter = DatePart;
base.Binding = b;
}
}
}
I might not understand what you're exactly trying to do, but this seems like a lot of work to just view/edit the DatePart. Would using a formatter and a DataGridTextColumn be easier? WPF Binding StringFormat Short Date String
First, please use debug to check your binding path whether it is ok.
Second, check your FromDate property whether is triggered.
Third, please use Snoop to monitor this DP whether is updated.
Last, i remember DataGrid has dynamic binding bug, so i am not sure above steps can solve your problem. If it is not ok , please let me know.
I would recommend Darlene approach if you only need to format the value. If you really need a converter for some magic I would recommend to set it via your binding in the XAML (I don't really know why you are trying to set it in the property definition).
Your XAML will be something like this:
<my:DataGridTimeColumn Header="From" Binding="{Binding FromDate, Converter={StaticResource myTimeConverter}} />
TimeConverter must implement IValueConverter and an instance must be defined in your window/datagrid resources. You can also define the TwoWay in that binding.
You can find a full example here.
In case you want to combine two or more pieces of data you can use a MultiBinding.
Tried to understand what you want to achieve from the comments and if I got it right you can do it using only the view model and a converter with a small workaround:
In your converter ConvertBack method, you can try to figure out if the value entered by the user has only the time part and if so set the date to something that will not be used (for example DateTime.MinValue):
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var convertedDateTime = DateTime.Parse(value.ToString());
var hasOnlyTime = value.ToString().Trim().Length <= 5; // This should be replaced with something more consistent
if (hasOnlyTime)
return new DateTime(DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day, convertedDateTime.Hour, convertedDateTime.Minute, convertedDateTime.Second);
return convertedDateTime;
}
Then in the object setter for the date you can check that and use the required value, if the date part is DateTime.MinValue change it back to the previous one, if not leave it as it is:
private DateTime _fromDate;
public DateTime FromDate
{
get { return _fromDate; }
set
{
if (value.Date != DateTime.MinValue)
_fromDate = value;
else
_fromDate = new DateTime(_fromDate.Year, _fromDate.Month, _fromDate.Day, value.Hour, value.Minute, value.Second);
OnPropertyChanged("FromDate");
}
}
Tested on .NET 4.5 and works as expected.
I have a parameterised constructor in My Application. I want to add controls dynamically to my silverlight Child Control Page. But it gives NullReferenceException.
I can't find out why it returns null.Can any help me with this situation?
public PDFExport(FrameworkElement graphTile1, FrameworkElement graphTile2,FrameworkElement graphTile3)
{
Button btnGraph1 = new Button();
string Name = graphTile1.Name;
btnGraph1.Content = Name;
btnGraph1.Width = Name.Length;
btnGraph1.Height = 25;
btnGraph1.Click += new RoutedEventHandler(btnGraph1_Click);
objStack.Children.Add(btnGraph1);
LayoutRoot.Children.Add(objStack); // Here am getting null Reference Exception
_graphTile1 = graphTile1;
_graphTile2 = graphTile2;
_graphTile3 = graphTile3;
}
Thanks.
I guess objStack is a stackpanel declared in your XAML?
Be aware that the UI component of your xaml are build by the call to InitializeComponent.
Thus objStack will not exist until you call InitializeCOmponent() in your constructor.
Also, you should know that the call to InitializeComponent is asynchronous, so you code should look like something like that:
private readonly FrameworkElement _graphTile1;
private readonly FrameworkElement _graphTile2;
private readonly FrameworkElement _graphTile3;
public PDFExport(FrameworkElement graphTile1, FrameworkElement graphTile2, FrameworkElement graphTile3)
{
_graphTile1 = graphTile1;
_graphTile2 = graphTile2;
_graphTile3 = graphTile3;
}
private void PDFExport_OnLoaded(object sender, RoutedEventArgs e)
{
Button btnGraph1 = new Button();
string Name = _graphTile1.Name;
btnGraph1.Content = Name;
btnGraph1.Width = Name.Length;
btnGraph1.Height = 25;
btnGraph1.Click += new RoutedEventHandler(btnGraph1_Click);
objStack.Children.Add(btnGraph1);
LayoutRoot.Children.Add(objStack);
}
Hope it helps.
As per my research i got that, why it raises an exception: Because there is no
InitializeComponent() in My Constructor and am not calling parent constructor.
That is the reason it raises Exception.
Just Add InitializeComponent() to the code, simple
What is the order in which attached properties are applied to an object ? I guess I should ignore this, but here my scenario:
I've got an attached property to stick the VM to the View, and then, another attached property that depend on the first one. I'm trying to see what happen if the second is set up before the first, but I can't manage to get the error! ie the first ( the model ) is always set up before the second, whatever is the order in xaml. Who is driving the order of assigment? Can I change it?
Now I'm dealing with the late assigmement by subscribing the proeprty change event:
DependencyPropertyDescriptor dd = DependencyPropertyDescriptor.FromProperty(FrameworkElement.DataContextProperty,depo.GetType());
dd.AddValueChanged(depo, (s, a) =>
{
ChangeDatacontext(s as DependencyObject);
}
and for simulate the problem I setup manually a new datacontext to the object.
Thanks,
Felix
I can't directly answer this question, because I never rely on which property is set before the other, but you can manage things with a method that both attached properties use.
here is an example from my current code:
public static readonly DependencyProperty RuleVMProperty =
DependencyProperty.RegisterAttached("RuleVM", typeof(DocumentRuleViewModel), typeof(DocumentRuleViewModel), new UIPropertyMetadata(null, RuleVMChanged));
public static void RuleVMChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var el = GetRefid(sender);
var vm = args.NewValue as DocumentRuleViewModel;
if(vm==null)
return;
vm.SetDocumentFromRefid(sender, el);
}
public static readonly DependencyProperty RefidProperty =
DependencyProperty.RegisterAttached("Refid", typeof(XmlElement), typeof(DocumentRuleViewModel), new UIPropertyMetadata(RefidChanged));
public static void RefidChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var el = args.NewValue as XmlElement;
var vm = GetRuleVM(sender);
if (vm == null)
return;
vm.SetDocumentFromRefid(sender, el);
}
private void SetDocumentFromRefid(DependencyObject sender, XmlElement element)
{
... // this is where the actual logic sits
}
so essentially you have two changed handlers and whichever triggers last executes the logic because it sees if the other property is null.
I have a GridView and want to serialize column widths across sessions. My idea of how to accomplish this is to attach a behavior to the GridViewColumns in such a way that each time the width of a column is changed the attached event handler is called and stores the new width. This already works well.
The only remaining problem:
How do I know in the event handler which GridViewColumn sent the event? I obviously need to know that in order to be able to store the width and later set the width on the correct column when restoring. Ideally I would like to use the name specified in XAML as column identifier.
Here is my code. XAML:
<GridView>
<GridViewColumn x:Name="GridColumn0"
HeaderTemplate="{StaticResource GridViewHeaderTemplate}" HeaderContainerStyle="{StaticResource GridViewHeaderStyle}"
Header="{x:Static strings:Strings.MainWindow_AppLog_Header_Severity}"
behaviors:GridViewBehaviors.PersistColumnWidth="True">
C# (please scroll down - question at bottom):
// Register the property used in XAML
public static readonly DependencyProperty PersistColumnWidthProperty =
DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(bool), typeof(GridViewBehaviors),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnPersistColumnWidthChanged)));
// Provide read access to the value
public static bool GetPersistColumnWidth(DependencyObject d)
{
return (bool)d.GetValue(PersistColumnWidthProperty);
}
// Provide write access to the value (set from XAML)
public static void SetPersistColumnWidth(DependencyObject d, bool value)
{
d.SetValue(PersistColumnWidthProperty, value);
}
// This gets called once when the XAML is compiled to BAML
// Set the event handler
private static void OnPersistColumnWidthChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
GridViewColumn column = sender as GridViewColumn;
if (column == null)
return;
// Couple the UI event with a delegate
if ((bool)args.NewValue)
((INotifyPropertyChanged)column).PropertyChanged += new PropertyChangedEventHandler(PersistWidth);
else
((INotifyPropertyChanged)column).PropertyChanged -= new PropertyChangedEventHandler(PersistWidth);
}
// Deal with the events
static void PersistWidth(object sender, PropertyChangedEventArgs e)
{
GridViewColumn column = sender as GridViewColumn;
if (column == null)
return;
// We are only interested in changes of the "ActualWidth" property
if (e.PropertyName != "ActualWidth")
return;
// Ignore NaNs
if (column.ActualWidth == double.NaN)
return;
// Persist the width here
// PROBLEM:
// How to get a unique identifier for column, ideally its name set in XAML?
}
I don't think you will be able to access the x:Name, but you should be able to define your own column type that has a Name property that you can set.
So instead of adding GridViewColumns you add NamedColumn objects in your xaml.
Define a type that derives from GridViewColumn:
public class NamedColumn : GridViewColumn
{
public string ColumnName {get; set;}
}
And use it in your xaml:
<GridView>
<NamedColumn ColumnName="GridColumn0" .... blablalba more stuff here />
...
NoW you should be able to cast the sender to a NamedColumn and access its name property.
can you use column.Header.ToString()? see here for why name from XAML isn't possible: How to get x:Name value runtime
Thanks for your answers, Robert, Thomas and Rune. I like Runes answer, but I found something even easier for my situation here:
I changed the type of the attached property from bool to string and simply store the name of the column there. The relevant changes are below.
XAML:
<GridViewColumn
behaviors:GridViewBehaviors.PersistColumnWidth="MainWindow_AppLog_Column0">
C#:
public static readonly DependencyProperty PersistColumnWidthProperty =
DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(string),
typeof(GridViewBehaviors), new FrameworkPropertyMetadata(string.Empty,
new PropertyChangedCallback(OnPersistColumnWidthChanged)));
static void PersistWidth(object sender, PropertyChangedEventArgs e)
{
// This now yields "MainWindow_AppLog_Column0"
string columnID = GetPersistColumnWidth(column);
I'm not sure it would work, but you can try something like that:
INameScope nameScope = NameScope.GetNameScope(column);
string name = nameScope
.Where(kvp => kvp.Value == column)
.Select(kvp => kvp.Key.ToString())
.FirstOrDefault();