c# Override a dependency property of an system control - c#

Hi I have read through some of the relevant questions in stackoverflow but still can't figure out how to solve my questions:
I need to creat a CheckableGroupBox control which is same as GroupBox but has a checkbox in the header. And it has the following requirement:
The original GroupBox has a Header property that is of Object type, I need to limit this property in the CheckableGroupBox to be string only.
Users can change the header's text (the checkbox's text) by calling myCheckableGroupBox.Header="some text here".
I wrote the below code to serve these proposes:
public class CheckableGroupBox : System.Windows.Controls.GroupBox
{
//override the default header property
private static void OnHeaderChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
CheckableGroupBox me = o as CheckableGroupBox;
me.labHeader.Content = e.NewValue.ToString();
}
public CheckableGroupBox() : base()
{
//setup the header of the group box
//create the stack panel, add the checkbox and the label
System.Windows.Controls.StackPanel sp = new System.Windows.Controls.StackPanel();
sp.Orientation = System.Windows.Controls.Orientation.Horizontal;
this.chkHeader = new System.Windows.Controls.CheckBox();
sp.Children.Add(this.chkHeader);
sp.Children.Add(this.labHeader);
//set the header to be the stack panel.
this.Header = sp;
//override the default HeaderProperty
CheckableGroupBox.HeaderProperty.OverrideMetadata(
typeof(object),
new PropertyMetadata("", OnHeaderChangedCallback)
);
}
}
However, the code doesn't work. When I add a CheckableGroupBox(in the design view) to a window, it prompts:
Cannot create an instance of "CheckableGroupBox", Object' type must derive from DependencyObject.
Any ideas?

You should use:
System.Windows.Controls.GroupBox.HeaderProperty.OverrideMetadata(
typeof(CheckableGroupBox),
new PropertyMetadata("", OnHeaderChangedCallback)
);

[CommonDependencyProperty]
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof (Control), (PropertyMetadata) new FrameworkPropertyMetadata((object) SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits));

Related

I tried to link TextBox to my Rod class, but changing the TextBox.Text will not change the Rod fields. Whats wrong?

I am dynamically creating rows using a Grid, which is a nested GroupBox, which has these custom TextBoxes. I bind it to my Rod class, but tried to change the fields of the TextBox.Text does not change the fields of Rod
public RodTextBox(string text,string propertyName, Rod rod)
{
Text = text;
TextWrapping = TextWrapping.Wrap;
addBinding(propertyName ,rod);
}
public void addBinding(string propertyName, Rod rod )
{
Binding myBinding = new Binding($"{propertyName}");
myBinding.Source = rod;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myBinding.Mode = BindingMode.TwoWay;
SetBinding(TextBlock.TextProperty, myBinding);
}
}
We would need to see your definition of Rod, and of course your call to RodTextBox.
Just guessing though, maybe the property in Rod you are binding to doesn't have {get; set;}?
In Visual Studio, you can also enable WPF binding errors (tools->options->Debugging->WPF Trace Settings->DataBinding to All) and see if the console shows any binding errors.

Custom Control returning task from another function Picker Control

I am developing a Custom Control part of dialog kit fork however I am having a bit of trouble here.
I have included item source as a property of the constructor which works but my question is how do I pass back the value of the picker control to the constructor.
public PickerView(string title, string message, IEnumerable ItemSource, string text = null, Keyboard keyboard = null)
{
InitializeComponent();
txtInput.Text = text;
BindTexts(title, message);
txtInput.Keyboard = keyboard;
pickItems.ItemsSource = ItemSource.Cast<object>().ToList();
pickItems.SelectedIndexChanged += PickItems_SelectedIndexChanged;
}
The way the control is instantiated is through the interface of
Task<T> GetPickerChoice<T>(string title, string message, IEnumerable ItemSource, string currentText = null,
Keyboard keyboard = null);
I want to be able to get the result that the user has selected out of the xamrian picker control which gets set in this event
private void PickItems_SelectedIndexChanged(object sender, EventArgs e)
{
throw new NotImplementedException();
}
I need some way of passing the result back to the way it is initiated this I pass in a list as follows.
List<PickerModel> _testList = new List<PickerModel>();
PickerModel model = new PickerModel();
model.Value = 1008;
model.Description = "FW";
_testList.Add(model);
var returnValueFromPicker= await Plugin.DialogKit.CrossDiaglogKit.Current.GetPickerChoice<PickerModel>("Fuel", $"This item is in one or more bins please select a bin location", _testList, null, Keyboard.Numeric);
I want the value of the picker to be in the value of returnValueFromPicker.
What I ended up doing was adding an event handler
public event EventHandler<string> Picked;
To which when the person clicked the ok button of the popup I basically invoked it and returned the selected item for the control in case anyone else finds this usefull.
private void Confirm_Clicked(object sender, EventArgs e)
{
Picked?.Invoke(this, pickItems.SelectedItem.ToString());
}
You could have also added a bindable command to your PickerView. With that approach, you can bind ViewModel's command to it (if you use MVVM pattern).
To do that, you may want to add
public static BindableProperty ConfirmedCommandProperty = BindableProperty.Create(
propertyName: nameof(ConfirmedCommand),
returnType: typeof(ICommand),
declaringType: typeof(YourPickerClass),
defaultValue: null);
public ICommand ConfirmedCommand
{
get { return (ICommand)GetValue(ConfirmedCommandProperty); }
set { SetValue(ConfirmedCommandProperty, value); }
}
to your picker. Then you can bind on a view layer.

How should I "pause" the program to show a tip to the user?

When a user is running my program for the first time, I want them to go through a series of tips. Each time they hit a certain "checkpoint", the program will pause what its doing, the background will go a little fuzzy (except for the area of the window the tip is referencing), and a tip will appear on top, explaining how to use it / what to do etc.
I dont quite know what to call this, in my head its called "tutorial tips", but googling anything related to this shows a load of generic tutorials with WPF / C#.
What would be the best way to do this? Am I really just looking at using popups and controling when they are visible? Is there a better / more elegant solution, or any resources out there to aid with this?
Ok, I think I may have dedicated too much time to this, but it sounded like a cool challenge :P
I've created a Decorator class named TipFocusDecorator that handles all this.
public class TipFocusDecorator : Decorator
{
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
// Using a DependencyProperty as the backing store for Open. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(TipFocusDecorator),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsOpenPropertyChanged));
public string TipText
{
get { return (string)GetValue(TipTextProperty); }
set { SetValue(TipTextProperty, value); }
}
// Using a DependencyProperty as the backing store for TipText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TipTextProperty =
DependencyProperty.Register("TipText", typeof(string), typeof(TipFocusDecorator), new UIPropertyMetadata(string.Empty));
public bool HasBeenShown
{
get { return (bool)GetValue(HasBeenShownProperty); }
set { SetValue(HasBeenShownProperty, value); }
}
// Using a DependencyProperty as the backing store for HasBeenShown. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasBeenShownProperty =
DependencyProperty.Register("HasBeenShown", typeof(bool), typeof(TipFocusDecorator), new UIPropertyMetadata(false));
private static void IsOpenPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var decorator = sender as TipFocusDecorator;
if ((bool)e.NewValue)
{
if (!decorator.HasBeenShown)
decorator.HasBeenShown = true;
decorator.Open();
}
if (!(bool)e.NewValue)
{
decorator.Close();
}
}
TipFocusAdorner adorner;
protected void Open()
{
adorner = new TipFocusAdorner(this.Child);
var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
adornerLayer.Add(adorner);
MessageBox.Show(TipText); // Change for your custom tip Window
IsOpen = false;
}
protected void Close()
{
var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
adornerLayer.Remove(adorner);
adorner = null;
}
}
This Decorator must be used in XAML around the control you want to focus. It has three properties: IsOpen, TipText and HasBeenShown. IsOpen must be set to true to make the focus and tip window appear (and is set to false automatically when the tip window is closed). TipText allows you to define the text that must be shown in the tip window. And HasBeenShown keeps track of whether the tip window has been shown, so it only shows once. You can use Bindings for all these properties or set them from code-behind.
To create the focus effect, this class uses another custom Adorner, the TipFocusAdorner:
public class TipFocusAdorner : Adorner
{
public TipFocusAdorner(UIElement adornedElement)
: base(adornedElement)
{
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var root = Window.GetWindow(this);
var adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement);
var presentationSource = PresentationSource.FromVisual(adornerLayer);
Matrix transformToDevice = presentationSource.CompositionTarget.TransformToDevice;
var sizeInPixels = transformToDevice.Transform((Vector)adornerLayer.RenderSize);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(sizeInPixels.X), (int)(sizeInPixels.Y), 96, 96, PixelFormats.Default);
var oldEffect = root.Effect;
var oldVisibility = AdornedElement.Visibility;
root.Effect = new BlurEffect();
AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, Visibility.Hidden);
rtb.Render(root);
AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, oldVisibility);
root.Effect = oldEffect;
drawingContext.DrawImage(rtb, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(22, 0, 0, 0)), null, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
drawingContext.DrawRectangle(new VisualBrush(AdornedElement) { AlignmentX = AlignmentX.Left, TileMode = TileMode.None, Stretch = Stretch.None },
null,
AdornedElement.RenderTransform.TransformBounds(new Rect(AdornedElement.RenderSize)));
}
}
This dims and blurs (and freezes, since it actually uses a screen capture) all the window, while keeping the desired controls focused and clear (and moving - i.e. in TextBoxes, the text input caret will still be visible and blinking).
To use this Decorator, you only must set it like this in XAML:
<StackPanel>
<local:TipFocusDecorator x:Name="LoginDecorator"
TipText="Enter your username and password and click 'Login'"
IsOpen="{Binding ShowLoginTip}">
<local:LoginForm />
</local:TipFocusDecorator>
</StackPanel>
And the final result, when ShowLoginTip is set to true:
KNOWN ISSUES
Right now this uses a simple MessageBox to show the tip, but you can create your own Window class for the tips, style it as you want, and call it with ShowDialog() instead of the MessageBox.Show() (and you could also control where the Window appears, if you want it to appear right next to the focused Control or something like that).
Also, this won't work inside UserControls right away, because AdornerLayer.GetAdornerLayer(AdornedElement) will return null inside UserControls. This could be easily fixed by looking for the AdornerLayer of the PARENT of the UserControl (or the parent of the parent, recursively). There are functions around to do so.
This won't work for Pages either, only for Windows. Simply because I use Window.GetWindow(this) to get the parent Window of the Decorator... You could use other functions to get the parent, that could work either with Windows, Pages or whatever. As with the AdornerLayer problem, there are plenty of solutions for this around here.
Also, I guess this could be animated somehow (making the blur and dim effect appear gradually, for instance), but haven't really looked into it...
You can create your tip as a window and show it using ShowDialog(). This gives you a Modal dialog, as others have suggested. Be sure to set it's owner. Just before you show it, you can use
<UIElement.Effect>
<BlurEffect/>
</UIelement.Effect>
to set your window or outer container's(grid maybe) Blur Effect. The radius property sets the "level" of blur, so I imagine you can set it to 0 initially and modify it programatically when you show your dialog

How can I add a hint or tooltip to a label in C# Winforms?

It seems that the Label has no Hint or ToolTip or Hovertext property. So what is the preferred method to show a hint, tooltip, or hover text when the Label is approached by the mouse?
You have to add a ToolTip control to your form first. Then you can set the text it should display for other controls.
Here's a screenshot showing the designer after adding a ToolTip control which is named toolTip1:
yourToolTip = new ToolTip();
//The below are optional, of course,
yourToolTip.ToolTipIcon = ToolTipIcon.Info;
yourToolTip.IsBalloon = true;
yourToolTip.ShowAlways = true;
yourToolTip.SetToolTip(lblYourLabel,"Oooh, you put your mouse over me.");
System.Windows.Forms.ToolTip ToolTip1 = new System.Windows.Forms.ToolTip();
ToolTip1.SetToolTip( Label1, "Label for Label1");
just another way to do it.
Label lbl = new Label();
new ToolTip().SetToolTip(lbl, "tooltip text here");
Just to share my idea...
I created a custom class to inherit the Label class. I added a private variable assigned as a Tooltip class and a public property, TooltipText. Then, gave it a MouseEnter delegate method. This is an easy way to work with multiple Label controls and not have to worry about assigning your Tooltip control for each Label control.
public partial class ucLabel : Label
{
private ToolTip _tt = new ToolTip();
public string TooltipText { get; set; }
public ucLabel() : base() {
_tt.AutoPopDelay = 1500;
_tt.InitialDelay = 400;
// _tt.IsBalloon = true;
_tt.UseAnimation = true;
_tt.UseFading = true;
_tt.Active = true;
this.MouseEnter += new EventHandler(this.ucLabel_MouseEnter);
}
private void ucLabel_MouseEnter(object sender, EventArgs ea)
{
if (!string.IsNullOrEmpty(this.TooltipText))
{
_tt.SetToolTip(this, this.TooltipText);
_tt.Show(this.TooltipText, this.Parent);
}
}
}
In the form or user control's InitializeComponent method (the Designer code), reassign your Label control to the custom class:
this.lblMyLabel = new ucLabel();
Also, change the private variable reference in the Designer code:
private ucLabel lblMyLabel;
I made a helper to make life easier.
public static class ControlUtilities1
{
public static Control AddToolTip(this Control control, string title, string text)
{
var toolTip = new ToolTip
{
ToolTipIcon = ToolTipIcon.None,
IsBalloon = true,
ShowAlways = true,
ToolTipTitle = title,
};
toolTip.SetToolTip(control, text);
return control;
}
}
Call it after controls are ready:
InitializeComponent();
...
linkLabelChiValues.AddToolTip(title, text);
It's an way to keep consistent tool tip styles.

FrameworkElementFactory "ignores" parent resources (e.g. styles)

I am trying to create some custom treeviews. Everything is working fine so far, but I got a little problem with styles. I have a simple "RedBackground" Style which I add to the resources of the Window. When adding normal elements, it works fine.
When using a custom item template to render treeview items, my resource is ignored. If I add the resource directly to the template it works fine (as marked in code)...
I obviously do not want to have to add styles to the ItemTemplate direclty, would be very complicated in further development. I think I am missing some kind of "Binding" or "Lookup"... I think it is related to dependency properties... Or something in this direction.
Perhaps anyone has more insights, here is the code creating the template (inside util class, but thats just to keep all clean):
var hdt = new HierarchicalDataTemplate(t)
{
ItemsSource = new Binding("Children")
};
var tb = new FrameworkElementFactory(typeof (TextBlock));
tb.SetBinding(TextBlock.TextProperty, new Binding("Header"));
hdt.VisualTree = tb;
// This way it works...
TextBlockStyles.AddRedBackground(hdt.Resources);
return hdt;
And here my very simple custom tree view
public class TreeViewCustom<T> : TreeView
{
public TreeViewCustom()
{
MinWidth = 300;
MinHeight = 600;
ItemTemplate = TreeViewTemplates.TryGetTemplate(typeof(T));
// This is ignored.... (Also when set as resource to window)
TextBlockStyles.AddRedBackground(Resources);
}
}
Ok, and to be sure, here the code which creates the Style:
public static class TextBlockStyles
{
public static void AddRedBackground(ResourceDictionary r)
{
var s = CreateRedBackground();
r.Add(s.TargetType, s);
}
private static Style CreateRedBackground()
{
var s = new Style(typeof(TextBlock));
s.Setters.Add(new Setter
{
Property = TextBlock.BackgroundProperty,
Value = new SolidColorBrush(Colors.Red)
});
return s;
}
}
Thanks for any tips...
Chris
Is this a problem with "inheritance"? Not all properties are inherited, read more here:
Property Value Inheritance: http://msdn.microsoft.com/en-us/library/ms753197.aspx

Categories

Resources