Creating ControlTemplate programmatically - wpf

I have a simple set up like this.
ContentControl in XAML
Add AdornerY to the adorner layer in the code-behind to the ContentControl
AdornerY's template is set to my custom ControlTemplate AdornerTemplateY
I'm quite sure 1 and 2 shall not give any problems so I'm posting the code here for the 3.
Visual Studio Express is not very friendly with errors and it merely says:
A first chance exception of type 'System.ArgumentException' occurred
in PresentationFramework.dll
What's wrong with the code? Thanks a bunch.
class AdornerTemplateY : ControlTemplate
{
FrameworkElementFactory Chrome;
public AdornerTemplateY(ContentControl designerItem)
: base(typeof(AdornerY))
{
Chrome = new FrameworkElementFactory(typeof(Rectangle));
Chrome.SetValue(Rectangle.NameProperty, "INTERNAL_CHROME");
Chrome.SetValue(Rectangle.FillProperty, Brushes.PowderBlue);
Chrome.SetValue(Rectangle.StrokeProperty, Brushes.Black);
Chrome.SetValue(Rectangle.DataContextProperty, designerItem);
Chrome.SetValue(Rectangle.IsHitTestVisibleProperty, true);
this.VisualTree = this.Chrome;
this.Triggers.Add(CreateTrigger());
}
private Trigger CreateTrigger()
{
Trigger TriggerFocus = new Trigger
{
Property = AdornerY.IsMouseOverProperty,
Value = true,
Setters =
{
new Setter
{
Property = AdornerY.VisibilityProperty,
Value = Visibility.Collapsed
},
}
};
return TriggerFocus;
}
}

Related

livecharts piechart mvvm databinding

I am having trouble attempting to perform a seemingly simple series databind on a livecharts piechart. The error I"m seeing in output is -
An unhandled exception of type 'System.NullReferenceException'
occurred in LiveCharts.dll Object reference not set to an instance of
an object.
Here is my binding -
<lvc:PieChart LegendLocation="None" Series="{Binding TodayValues}"
DataTooltip="{x:Null}">
</lvc:PieChart>
And here is my ViewModel property -
private SeriesCollection _todayValues;
public SeriesCollection TodayValues
{
get { return _todayValues; }
set { SetNotifyingProperty(() => TodayValues, ref _todayValues, value); }
}
private void LoadInventoryPieCharts()
{
_todayValues = new SeriesCollection
{
new PieSeries
{
Values = new ChartValues<double>{ 21 }
},
new PieSeries
{
Values = new ChartValues<double>{ 4 }
}
};
}
SetNotifyingProperty is just an iplementation of INotifyPropertyChanged and the LoadInventoryPieCharts gets called when the VM is loaded.
Any thoughts on what is missing that is causing a nullreferenceexceptio?

Setting Style property in code - dependency property FontSizeProperty name does not exist in current context in silverlight library

This is similar to my previous question, but that solution did not solve this problem.
fontSizeProperty is not being recognized when I move a method from my Silverlight MainPage code behind (which worked) to a new class in a silverlight library
using System.Windows.Controls;
namespace MyNameSpace
{
public static class DataGridBuilder
{
private static Style BuildHeaderStyle(string tooltip)
{
Style newGridHeaderStyle = new Style(typeof(DataGridColumnHeader));
newGridHeaderStyle.Setters.Add(new Setter { Property = FontSizeProperty, Value = 9.0 });
newGridHeaderStyle.Setters.Add(new Setter { Property = FontWeightProperty, Value = FontWeights.Bold });
return newGridHeaderStyle;
}
}
}
NOTE: Per MSDN for FontSizeProperty, I do include System.Windows reference, and "using System.Windows.Control"
Based on answers below, I changed "Property = FontSizeProperty" to "Property=DataGridColumnHeader.FontSizeProperty" etc., like this:
private static Style BuildHeaderStyle(string tooltip)
{
FontWeight fw = FontWeights.Bold;
Style newGridHeaderStyle = new Style(typeof(DataGridColumnHeader));
newGridHeaderStyle.Setters.Add(new Setter { Property = DataGridColumnHeader.FontSizeProperty, Value = 9.0 });
newGridHeaderStyle.Setters.Add(new Setter { Property = DataGridColumnHeader.FontWeightProperty, Value = FontWeights.Bold });
newGridHeaderStyle.Setters.Add(new Setter { Property = DataGridColumnHeader.ContentTemplateProperty, Value = CreateDataGridHeaderTemplate(tooltip) });
return newGridHeaderStyle;
}
I believe you want Control.FontSizeProperty and Control.FontWeightProperty instead.
Your MainPage is a user control, which has Control as a superclass and hence inherits the above two dependency properties. Your static class isn't a subclass of Control so it doesn't inherit these dependency properties.
FontSizeProperty is defined on Control, which you do not derive from, so you have to use Control.FontSizeProperty.

C# WPF MVVM: Calling an Object

I have a QuestionsFile class, which has questions, a name etc...
Now I have created a wpf application and I have a window which has different pages. These pages inherit the BaseViewModel class. I create my pages in the WindowViewModel class (where I pass a new QuestionsFile object to the constructor and dispatch it through the constructors of the other viewmodels):
var chooseQFVM = new ChooseQuestionsFileViewModel(this.QuestionsFile);
var showChosenQFVM = new ShowChosenQuestionsFileViewModel(this.QuestionsFile);
var pages2 = new List<WizardControlViewBaseModel>();
pages2.Add(chooseQFVM);
pages2.Add(showChosenQFVM);
pages = new ReadOnlyCollection<WizardControlViewBaseModel>(pages2);`
All these viewmodels inherit the BaseViewModel class which has the QuestionsFile property.
so when let's say I change the QuestionsFile property of the chooseQFView variable from a combobox and I give it through - the same property in the BaseBiewModel class must be changed. So this is this code of the property in the BaseBiewModel class:
public QuestionsFile QuestionsFile
{
get { return qfile; }
set
{
qfile = value;
OnPropertyChanged("QuestionsFile");
}
}
So I call the PropertyChanged event, but when i want to call this property in the next page and I debug it says that all the properties of my QuestionsFile are null..
this is the code where I change the value of the QuestionsFile property:
public OptionViewModel<QuestionsFile> SelectedFile
{
get
{
return selectedFile;
}
set
{
selectedFile = value;
OnPropertyChanged("SelectedFile");
this.QuestionsFile = value.GetValue();
}
}
From what I understand on your view you have a collection of QuestionFiles (via OptionsViewModel) that you are selecting one of them via the SelectedFile property. At this point in time you are setting the chosen QuestionFile property.
This is all good for the ChooseQuestionViewModel (which I assume contains the SelectedFile property). I am not sure this is your issue but the SelectedFile will not reflect inside the ShowQuestionsFileViewModel because now both view models refer to a different instance.
Is this your issue? That you cannot see change of the selected QuestionFile in the ShowQuestionsFileViewModel?
If so you will need to tell the other view model that it has changed via events or having it reference the ChooseQuestionViewModel so it can listen for the PropertyChanged event so you can grab the selected item from and update itself.
i.e.
//Ctor
public ShowQuestionsFileViewModel(ChooseQuestionViewModel chooseViewModel)
{
_chooseViewModel = chooseViewModel;
chooseViewModel.PropertyChanged += ChoosePropertyChanged
}
private void ChoosePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.Property == "SelectedFile")
{
this.SelectedFile = _chooseViewModel.SelecteFile;
}
}

WPF Validation Manually Adding Errors into Validation.Errors Collection

Is there any way to manually/dynamically add errors to the Validation.Errors collection?
from http://www.wpftutorial.net/ValidationErrorByCode.html
ValidationError validationError = new ValidationError(regexValidationRule,
textBox.GetBindingExpression(TextBox.TextProperty));
validationError.ErrorContent = "This is not a valid e-mail address";
Validation.MarkInvalid(
textBox.GetBindingExpression(TextBox.TextProperty),
validationError);
jrwren's answer guided me in the right direction, but wasn't very clear as to what regexValidationRule was nor how to clear the validation error. Here's the end result I came up with.
I chose to use Tag since I was using this manual validation in a situation where I wasn't actually using a viewmodel or bindings. This gave something I could bind to without worrying about affecting the view.
Adding a binding in code behind:
private void AddValidationAbility(FrameworkElement uiElement)
{
var binding = new Binding("TagProperty");
binding.Source = this;
uiElement.SetBinding(FrameworkElement.TagProperty, binding);
}
and trigging a validation error on it without using IDataError:
using System.Windows;
using System.Windows.Controls;
private void UpdateValidation(FrameworkElement control, string error)
{
var bindingExpression = control.GetBindingExpression(FrameworkElement.TagProperty);
if (error == null)
{
Validation.ClearInvalid(bindingExpression);
}
else
{
var validationError = new ValidationError(new DataErrorValidationRule(), bindingExpression);
validationError.ErrorContent = error;
Validation.MarkInvalid(bindingExpression, validationError);
}
}

Winforms DataBind to Control's Visible Property

Are there any known issues when databinding to a control's visible property?
The control is always NOT visible regardless of what my property is.
Public ReadOnly Property IsRibbonCategory() As Boolean
Get
Return True
End Get
End Property
I tried the control's text property and other properties and they seem to work correctly.
I am trying to set a Panel's visible property.
I've found that life is better if you assume that binding to a control's Visible property is broken, despite the fact that it sometimes works. See http://support.microsoft.com/kb/327305, which says as much (and while the KB article applies to .NET 1.0 and 1.1, it still seems to be a problem in at least 2.0).
I created a utility class for creating bindings which, among other things, gave me a centralized place to add a work-around. Instead of actually creating a binding on Visible it does two things:
It subscribes to the data source's INotifyPropertyChanged.PropertyChanged event and sets the Visible value as appropriate when the event is raised.
It sets the initial value of Visible according to the current data source value.
This required a little reflection code, but wasn't too bad. It is critical that you don't bind the Visible property and do the work-around or it won't work.
Workaround: Set the Visible property on the BindingComplete event.
I had same issue setting a label's Visible property - always stays false, even though setting the Enabled property works fine.
I just hit this issue in .NET 4.7.1 and Visual Studio 2017. To fix it, I changed the Visible property on my control to be initially set to True, as I had it as False previously.
Things to check:
Be sure you've instantiated the class that has the IsRibbonCategory property
Did you set the datasource of property of the binding source to the instance of the class
The datasource update mode should be on "on validation"
Make sure you didn't set the visible property manually to false on the control
Hope that helps. Can you post more code?
A workaround would be to use a Component to databind to a control's visiblity property instead of directly binding to the control's visibility property.
See below code:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public class ControlVisibilityBinding : Component
{
private static readonly object EventControlChanged = new object();
private static readonly object EventVisibleChanged = new object();
private System.Windows.Forms.Control _control;
private bool _visible = true;
public event EventHandler VisibleChanged
{
add { Events.AddHandler(EventVisibleChanged, value); }
remove { Events.RemoveHandler(EventVisibleChanged, value); }
}
public event EventHandler ControlChanged
{
add { Events.AddHandler(EventControlChanged, value); }
remove { Events.RemoveHandler(EventControlChanged, value); }
}
public ControlVisibilityBinding()
{
}
public ControlVisibilityBinding(IContainer container)
{
container.Add(this);
}
[DefaultValue(null)]
public System.Windows.Forms.Control Control
{
get { return _control; }
set
{
if(_control == value)
{
return;
}
WireControl(_control, false);
_control = value;
if(_control != null)
{
_control.Visible = _visible;
}
WireControl(_control, true);
OnControlChanged(EventArgs.Empty);
OnVisibleChanged(EventArgs.Empty);
}
}
[DefaultValue(true)]
public bool Visible
{
get { return _visible; }
set
{
if(_visible != value)
{
_visible = value;
}
if(Control != null)
{
Control.Visible = _visible;
}
OnVisibleChanged(EventArgs.Empty);
}
}
private void WireControl(Control control, bool subscribe)
{
if(control == null)
{
return;
}
if(subscribe)
{
control.VisibleChanged += Control_VisibleChanged;
}
else
{
control.VisibleChanged -= Control_VisibleChanged;
}
}
private void Control_VisibleChanged(object sender, EventArgs e)
{
OnVisibleChanged(EventArgs.Empty);
}
protected virtual void OnVisibleChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventVisibleChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
protected virtual void OnControlChanged(EventArgs e)
{
EventHandler subscribers = (EventHandler)Events[EventControlChanged];
if(subscribers != null)
{
subscribers(this, e);
}
}
}
static class Program
{
[STAThread]
static void Main()
{
using(Form form = new Form())
using(FlowLayoutPanel groupBoxLayoutPanel = new FlowLayoutPanel())
using(RadioButton visibleButton = new RadioButton())
using(RadioButton hiddenButton = new RadioButton())
using(GroupBox groupBox = new GroupBox())
using(Label text = new Label())
using(ControlVisibilityBinding visibilityBinding = new ControlVisibilityBinding())
using(TextBox inputTextBox = new TextBox())
{
groupBoxLayoutPanel.Dock = DockStyle.Fill;
groupBoxLayoutPanel.FlowDirection = FlowDirection.LeftToRight;
groupBoxLayoutPanel.AutoSize = true;
groupBoxLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
visibleButton.Text = "Show Label";
visibleButton.AutoSize = true;
hiddenButton.Text = "Hide Label";
hiddenButton.AutoSize = true;
groupBoxLayoutPanel.Controls.Add(visibleButton);
groupBoxLayoutPanel.Controls.Add(hiddenButton);
inputTextBox.Text = "Enter Label Text Here";
inputTextBox.Dock = DockStyle.Top;
groupBox.AutoSize = true;
groupBox.AutoSizeMode = AutoSizeMode.GrowAndShrink;
groupBox.Controls.Add(groupBoxLayoutPanel);
groupBox.Dock = DockStyle.Fill;
text.AutoSize = true;
text.ForeColor = Color.Red;
text.Dock = DockStyle.Bottom;
text.BorderStyle = BorderStyle.FixedSingle;
text.Font = new Font(text.Font.FontFamily, text.Font.Size * 1.25f, FontStyle.Bold | FontStyle.Italic);
text.DataBindings.Add("Text", inputTextBox, "Text", true, DataSourceUpdateMode.Never);
visibilityBinding.Control = text;
visibleButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
Binding binding = hiddenButton.DataBindings.Add("Checked", visibilityBinding, "Visible", true, DataSourceUpdateMode.OnPropertyChanged);
ConvertEventHandler invertConverter = (sender, e) => e.Value = !((bool)e.Value);
binding.Format += invertConverter;
binding.Parse += invertConverter;
form.Controls.Add(inputTextBox);
form.Controls.Add(text);
form.Controls.Add(groupBox);
Application.Run(form);
}
}
}
}
Here is my turn around, it may be stupid but it worked many times.
I put one Panel control in my form, I make it to Fill my form and I put everything in that Panel. All the controls I bind the Visible property see their visibility change according to the objects in my DataGridView.

Resources