I have a DataGrid that is made of dynamically by the user. That means each time it runs the columns could and will be very different. Each column is programmatically added for that reason. I need to add some DataTriggers to it so figured this would work:
Style style = new Style();
style.TargetType = typeof(DataGridTextColumn);
DataTrigger tg = new DataTrigger()
{
Binding = new Binding(value),
Value = "bad data"
};
tg.Setters.Add(new Setter()
{
Property = UIElement.VisibilityProperty,
Value = Visibility.Hidden
});
While this gives no errors in the IDE when run it crashes and gives me
'DataGridTextColumn' type must derive from FrameworkElement or FrameworkContentElement.
What is the correct way of adding a DataTrigger to a DataGridTextColumn Programmatically
You need to use typeof(DataGridCell). The trigger should be applied to the Cell itself, not the Column.
Related
Hi I want to set the Text property of a Textbox by code behind. At the moment I do using XAML:
<TextBox x:Name="txtFilter" Text="{Binding FiltroFunzioni, Mode=OneWayToSource}" Grid.Row="0" />
As test I did this:
Binding b = new Binding();
b.Mode = BindingMode.OneWayToSource;
b.Path = new PropertyPath("Text"); //??
b.Source = PageViewModel.FiltroFunzioni;
BindingOperations.SetBinding(txtFilter, TextBlock.TextProperty, b);
The variable "FiltroFunzioni" is a string defined as property:
private string _filtroFunzioni = "";
public string FiltroFunzioni
{
get { return _filtroFunzioni; }
set
{
_filtroFunzioni = value;
RaisePropertyChanged("FiltroFunzioni");
_functionsView.Refresh();
}
}
Basically I dunno what kind of value should I set as PropertyPath. Any ideas?
You don't need the PropertyPath here. If you just remove it, your binding should work.
That being said, you should bind in XAML wherever possible.
If your issue is that changes to FiltroFunzioni don't update your textbox, that's because your binding is specifically declared as OneWayToSource: that means that changing the UI changes the source, but changing the source doesn't change the UI. If that isn't what you want, set the Mode to something else, like "TwoWay" - then changes to the source change the UI, AND changes to the UI change the source.
EDIT:
If you really want to bind from your ViewModel instead of just using XAML, TwoWay binding requires utilizing the Path for some reason, when binding through C#. Either of the following solutions work:
b.Source = FiltroFunzioni;
b.Path = new PropertyPath(".");
b.Source = this;
b.Path = new PropertyPath("FiltroFunzioni");
Note that with TwoWay binding you have to either initialize your FiltroFunzioni by setting the TextBox.Text property in your XAML, or setting FiltroFunzioni after the binding was initialized. Otherwise, WPF will immediately override it from the (by default empty) Text in your TextBox.
I working on Xamarin project and made custom renderer for my custom control in UWP project. I found how to set the ControlTemplate by using xml code.
XML Way:
var tb = new TextBox(); // or what I do in Xamarin var tb = Control;
var ct = (Controls.ControlTemplate)XamlReader.Load(#"
<ControlTemplate TargetType=""TextBox"" xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid>
....
</Grid>
</ControlTemplate>");
tb.Template = ct;
But how I can do the same in code?
var tb = new TextBox(); // or what I do in Xamarin var tb = Control;
var ct = new ControlTemplate();
ct.TargetType = typeof(TextBox);
var grid = new Grid();
ct.VisualTree = grid // This is how it was done in wpf but there is no such option in UWP
tb.Template = ct;
It's not supported in UWP, and I previously found no way to directly set it. As per the MS docs.
ControlTemplate: this is used as the value of the Control.Template
property, which defines the visuals of a control by applying the
template. You almost always define a ControlTemplate as a XAML
resource, using an implicit key TargetType that is the same as a Style
that sets Control.Template with a Setter. You rarely if ever assign a
value for Control.Template directly on a control instance.
Besides possibly delving into reflection, or using the XAMLReader as per your first example, I have never found another way to do it, like you do in WPF.
Is it possible to attach datatriggers to a style at runtime? I've been through my (non-working) code a few times now and can't seem to find where I've gone wrong.
Here's the method I use to attach the style and trigger:
private void AttachVisibilityTrigger(Control ctrl)
{
Style stl = new System.Windows.Style();
DataTrigger dt = new DataTrigger();
PropertyInfo pi = _entity.GetType().GetProperty(this.SecondaryOptions[ctrl.Name]);
Type controlType = this.GetControlTypeForProperty(ref dt, pi); //gets the control type based on the property name and then sets the value for the DataTrigger for which I want the visibility to be hidden
Binding b = this.GetVisibilityBindingByControlType(controlType); //returns a new Binding with the appropriate Path set that corresponds to the bound property value (e.g IsChecked for CheckBoxes, Text for TextBoxes, SelectedValue for Comboboxes, etc)
b.ElementName = this.SecondaryOptions[ctrl.Name];
dt.Binding = b;
dt.Setters.Add(new Setter(Control.VisibilityProperty, System.Windows.Visibility.Hidden));
stl.Triggers.Add(dt);
ctrl.Style = stl;
}
I'm pretty sure the binding is just broken, i created similar styles in code and they work.
Especially this line looks quite suspicious:
b.ElementName = this.SecondaryOptions[ctrl.Name];
(If you want to bind to the control itself use RelativeSource instead.)
Have you checked the Output window of VisualStudio for binding errors?
I am implementing the WPF DataGrid (very new to WPF). I followed tutorials that showed how to bind the ComboBoxColumn using staticresources. However, the databinding for a few columns in my datagrid will not be known until runtime.
Because of this, I can't bind them with the staticresource. Is there any other way to databind the ComboBoxColumns in a DataGrid? In ASP.NET, I know we had the rowdatabound code where we could do this and dynamically create the contents of the columns. But, in WPF, it looks like everything is done through resources.
How can you databind using dynamic resources in the DataGrid?
Thanks!
You can set up bindings dynamically.
Something like this (this code creates grid view columns and assigns dynamic bindings)
private void AddColumn(GridView view, Field fld)
{
GridViewColumn col = new GridViewColumn();
col.Header = fld.Label;
Binding bnd = new Binding();
switch (fld.FieldType)
{
case FieldType.DateTime:
bnd.Converter = new DateTimeToDateStringConverter();
break;
// or some other converters
}
bnd.Path = new PropertyPath(string.Format("Fields[{0}]",
_table._fields.IndexOf(fld))); // the string matches what you would use in XAML
col.DisplayMemberBinding = bnd;
view.Columns.Add(col);
}
I know that TextBlock can present a FlowDocument, for example:
<TextBlock Name="txtFont">
<Run Foreground="Maroon" FontFamily="Courier New" FontSize="24">Courier New 24</Run>
</TextBlock>
I would like to know how to set a FlowDocument that is stored in a variable to a TextBlock.
I am looking for something like:
string text = "<Run Foreground="Maroon" FontFamily="Courier New" FontSize="24">Courier New 24</Run>"
txtFont.Text = text;
However, The result of the code above is that the XAML text is presented unparsed.
EDIT: I guess my question was not clear enough. What I'm really trying to achive is:
The user input some text into a RichTextBox.
The application saves the user input as FlowDocument from the RichTextBox, and serializes it to the disk.
The FlowDocument is deserialized from the disk to the variable text.
Now, I would like to be able to present the user text in a TextBlock.
Therefore, as far as I understand, creating a new Run object and setting the parameters manually will not solve my problem.
The problem is that serializing RichTextBox creates Section object, which I cannot add to TextBlock.Inlines. Therefore, it is not possible to set the deserialized object to TextProperty of TextBlock.
create and add the object as below:
Run run = new Run("Courier New 24");
run.Foreground = new SolidColorBrush(Colors.Maroon);
run.FontFamily = new FontFamily("Courier New");
run.FontSize = 24;
txtFont.Inlines.Add(run);
I know that TextBlock can present FlowDocument
What makes you think that ? I don't think it's true... The content of a TextBlock is the Inlines property, which is an InlineCollection. So it can only contain Inlines... But in a FlowDocument, the content is the Blocks property, which contains instances of Block. And a Block is not an Inline
If your FlowDocument has been deserialized, it means that you have an object of type FlowDocument, right? Try setting the Text property of your TextBlock to this value. Of course, you cannot do this with txtFont.Text = ..., since this only works for strings. For other types of objects, you need to set the DependencyProperty directly:
txtFont.SetValue(TextBlock.TextProperty, myFlowDocument)
Here is how we are setting the look of a textblock by assigning a style on-the-fly.
// Set Weight (Property setting is a string like "Bold")
FontWeight thisWeight = (FontWeight)new FontWeightConverter().ConvertFromString(Properties.Settings.Default.DealerMessageFontWeightValue);
// Set Color (Property setting is a string like "Red" or "Black")
SolidColorBrush thisColor = (SolidColorBrush)new BrushConverter().ConvertFromString(Properties.Settings.Default.DealerMessageFontColorValue);
// Set the style for the dealer message
// Font Family Property setting is a string like "Arial"
// Font Size Property setting is an int like 12, a double would also work
Style newStyle = new Style
{
TargetType = typeof(TextBlock),
Setters = {
new Setter
{
Property = Control.FontFamilyProperty,
Value = new FontFamily(Properties.Settings.Default.DealerMessageFontValue)
},
new Setter
{
Property = Control.FontSizeProperty,
Value = Properties.Settings.Default.DealerMessageFontSizeValue
},
new Setter
{
Property = Control.FontWeightProperty,
Value = thisWeight
},
new Setter
{
Property = Control.ForegroundProperty,
Value = thisColor
}
}
};
textBlock_DealerMessage.Style = newStyle;
You can eliminate the style section and set properties directly, but we like keeping things bundled in the style to help us organize the look throughout the project.
textBlock_DealerMessage.FontWeight = thisWeight;
HTH.