I have a Datagrid where some columns are DataGridTemplateColumn like this
<DataGridTemplateColumn
Width="1.5*"
Header="{x:Static lang:Labels.GENERAL_ValorTotal}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<sty:DecimalTextBox
Text="{Binding Valor,StringFormat=\{0:c3\}}"
IsReadOnly="True"
Style="{StaticResource DecimalTextBoxGridStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
My problem is when I select a row, My custom control doesn't change it's foreground color as DataGridTextColumn does.
How can i do to style my custom control or my datagrid to force all columns to change
Edit 1 My custom object.
public class DecimalTextBox : TextBox
{
#region Float Color
public static readonly DependencyProperty FloatColorProperty =
DependencyProperty.Register("FloatColor", typeof(Color), typeof(DecimalTextBox), new FrameworkPropertyMetadata(Colors.Red));
public Color FloatColor
{
get { return (Color)GetValue(FloatColorProperty); }
set { SetValue(FloatColorProperty, value); }
}
#endregion
#region Show Zero value
public bool ShowZeroValue
{
get { return (bool)GetValue(ShowZeroValueProperty); }
set { SetValue(ShowZeroValueProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowZeroValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowZeroValueProperty =
DependencyProperty.Register("ShowZeroValue", typeof(bool), typeof(DecimalTextBox), new PropertyMetadata(true));
#endregion
protected TextBlock _textBlock;
protected FrameworkElement _textBoxView;
public DecimalTextBox()
{
_textBlock = new TextBlock() { Margin = new Thickness(1, 0, 0, 0) };
Loaded += ExTextBox_Loaded;
}
private void ExTextBox_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= ExTextBox_Loaded;
// hide the original drawing visuals, by setting opacity on their parent
var visual = this.GetChildOfType<DrawingVisual>();
if (visual != null)
{
_textBoxView = (FrameworkElement)visual.Parent;
_textBoxView.Opacity = 0;
// add textblock to do the text drawing for us
var grid = this.GetChildOfType<Grid>();
if (grid.Children.Count >= 2)
grid.Children.Insert(1, _textBlock);
else
grid.Children.Add(_textBlock);
}
}
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
_textBoxView.Opacity = 0;
_textBlock.Visibility = Visibility.Visible;
MustShowValue();
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
_textBoxView.Opacity = 1;
_textBlock.Visibility = Visibility.Collapsed;
}
private bool MustShowValue()
{
bool show = true;
if (!ShowZeroValue && Text == "0")
{
show = false;
_textBlock.Inlines.Clear();
_textBlock.Inlines.Add(new Run
{
Text = string.Empty,
FontFamily = FontFamily,
FontSize = FontSize,
Foreground = Foreground
});
_textBlock.Inlines.Add(new Run
{
Text = string.Empty,
FontFamily = FontFamily,
TextDecorations = System.Windows.TextDecorations.Underline,
BaselineAlignment = BaselineAlignment.TextTop,
FontSize = FontSize * 5 / 6,
Foreground = new SolidColorBrush(FloatColor)
});
}
return show;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (MustShowValue())
{
// making sure text on TextBlock is updated as per TextBox
var dotPos = Text.IndexOf('.');
var textPart1 = dotPos == -1 ? Text : Text.Substring(0, dotPos + 1);
var textPart2 = (dotPos == -1 || dotPos >= (Text.Length - 1)) ? null : Text.Substring(dotPos + 1);
_textBlock.Inlines.Clear();
_textBlock.Inlines.Add(new Run
{
Text = textPart1,
FontFamily = FontFamily,
FontSize = FontSize,
Foreground = Foreground
});
if (textPart2 != null)
_textBlock.Inlines.Add(new Run
{
Text = textPart2,
FontFamily = FontFamily,
TextDecorations = System.Windows.TextDecorations.Underline,
BaselineAlignment = BaselineAlignment.TextTop,
FontSize = FontSize * 5 / 6,
Foreground = new SolidColorBrush(FloatColor)
});
}
}
}
public static class HelperExtensions
{
public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
}
<Style x:Key="DecimalTextBoxGridStyle" TargetType="{x:Type local:DecimalTextBox}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="FloatColor" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ShowZeroValue" Value="False"/>
<Style.Triggers>
<!--<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>-->
</Style.Triggers>
</Style>
You just need to update the inline(s) color (in inner TextBlock) every-time Foreground or FloatColor changes. The hard way would be add binding(s) between TextBox-properties and inline properties. The easy way would be to either add property changed callbacks to dependency property or just by overriding OnPropertyChanged:
For example (illustrates using OnPropertyChanged to keep control updated):
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == ForegroundProperty || e.Property == FloatColorProperty)
UpdateTextBlock(); //updates the text-block inlines
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
UpdateTextBlock(); // this can be merged to OnPropertyChanged (as text is also dependency property)
}
// new extracted method from OnTextChanged
private void UpdateTextBlock()
{
if (MustShowValue())
{
// making sure text on TextBlock is updated as per TextBox
var dotPos = Text.IndexOf('.');
var textPart1 = dotPos == -1 ? Text : Text.Substring(0, dotPos + 1);
var textPart2 = (dotPos == -1 || dotPos >= (Text.Length - 1)) ? null : Text.Substring(dotPos + 1);
_textBlock.Inlines.Clear();
_textBlock.Inlines.Add(new Run
{
Text = textPart1,
FontFamily = FontFamily,
FontSize = FontSize,
Foreground = Foreground
});
if (textPart2 != null)
_textBlock.Inlines.Add(new Run
{
Text = textPart2,
FontFamily = FontFamily,
TextDecorations = System.Windows.TextDecorations.Underline,
BaselineAlignment = BaselineAlignment.TextTop,
FontSize = FontSize * 5 / 6,
Foreground = new SolidColorBrush(FloatColor)
});
}
}
Same goes for FontFamily and FontSize too. This way whenever any property, that UpdateTextBlock() uses, is updated - whether through style or triggers - the control will know that it needs to update the inline(s) in inner TextBlock.
Update 08/27
Also, update your Style to set Foreground and FloatColor in setters, while using a MultiDataTrigger to account for both row's selected status and grid's focused state.
<Style x:Key="DecimalTextBoxGridStyle" TargetType="{x:Type local:DecimalTextBox}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FloatColor" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ShowZeroValue" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=IsKeyboardFocusWithin}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FloatColor" Value="White"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
Related
I've got this bit of XAML code. What it does is override the style of a button when mousing over. Here I use Green and PaleGreen for the sake of simplicity.
<Style TargetType="Button" x:Key="someNameHere">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="Green" BorderThickness="2,0,0,0">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="PaleGreen"/>
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
This works on both a standard button and more exotic ones, like so:
<Button Name="Standard" Content="Test" Style="{DynamicResource someNameHere}"/>
<Button Name="Exotic" Style="{DynamicResource someNameHere}">
<TextBlock Text="Test"/>
</Button>
<Button Name="MoreExotic" Style="{DynamicResource someNameHere}">
<Grid>
<TextBlock Text="Test"/>
</Grid>
</Button>
I want to do the same thing in code behind, on a custom button class. This is the code I have this far:
public class FlatButton : Button
{
public FlatButton() : base()
{
BorderThickness = new Thickness(2, 0, 0, 0);
SetStyle();
}
private void SetStyle()
{
Background = Brushes.PaleGreen;
Foreground = Brushes.Black;
BorderBrush = Brushes.Green;
Style style = new Style(typeof(Button), Style);
ControlTemplate template = new ControlTemplate(typeof(Button));
FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
border.SetValue(Border.BackgroundProperty, new TemplateBindingExtension(Button.BackgroundProperty));
border.SetValue(Border.BorderBrushProperty, BorderBrush);
border.SetValue(Border.BorderThicknessProperty, BorderThickness);
border.Name = "someNameHere";
template.VisualTree = border;
FrameworkElementFactory content = new FrameworkElementFactory(typeof(ContentPresenter));
content.SetValue(Border.VerticalAlignmentProperty, System.Windows.VerticalAlignment.Center);
content.SetValue(Border.HorizontalAlignmentProperty, System.Windows.HorizontalAlignment.Center);
border.AppendChild(content);
Trigger trigger = new Trigger()
{
Property = Button.IsMouseOverProperty,
Value = true
};
trigger.Setters.Add(new Setter()
{
TargetName = "someNameHere",
Property = Button.BackgroundProperty,
Value = Brushes.Green
});
trigger.Setters.Add(new Setter()
{
TargetName = "someNameHere",
Property = Button.ForegroundProperty,
Value = Brushes.White
});
template.Triggers.Add(trigger);
Setter setter = new Setter()
{
Property = Button.TemplateProperty,
Value = template
};
style.Setters.Add(setter);
Style = style;
}
And applying it to a similar set of buttons:
<local:FlatButton x:Name="Standard" Content="Test"/>
<local:FlatButton x:Name="Exotic">
<TextBlock Text="Test"/>
</local:FlatButton>
<local:FlatButton x:Name="MoreExotic">
<Grid>
<TextBlock Text="Test"/>
</Grid>
</local:FlatButton>
The problem is this works fine on the "standard" button, but it doesn't change the Foreground color of the elements inside the "exotic" buttons like the XAML version does, and I haven't been able to find a solution so far.
So does anyone know how do I set the Foreground color of my content in code?
The three use cases I'm interested in are plaintext, TextBlock, and TextBlock inside a container like a Grid or StackPanel.
The Style that you are creating programmatically is not equivalent to the one you have defined in the XAML markup. This is:
private void SetStyle()
{
Style style = new Style(typeof(Button), Style);
ControlTemplate template = new ControlTemplate(typeof(Button));
FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
border.SetValue(Border.BackgroundProperty, new TemplateBindingExtension(Button.BackgroundProperty));
border.SetValue(Border.BorderBrushProperty, Brushes.Green);
border.SetValue(Border.BorderThicknessProperty, new Thickness(2, 0, 0, 0));
template.VisualTree = border;
FrameworkElementFactory content = new FrameworkElementFactory(typeof(ContentPresenter));
content.SetValue(Border.VerticalAlignmentProperty, System.Windows.VerticalAlignment.Center);
content.SetValue(Border.HorizontalAlignmentProperty, System.Windows.HorizontalAlignment.Center);
border.AppendChild(content);
style.Setters.Add(new Setter()
{
Property = Button.BackgroundProperty,
Value = Brushes.PaleGreen
});
style.Setters.Add(new Setter()
{
Property = Button.ForegroundProperty,
Value = Brushes.Black
});
Trigger trigger = new Trigger()
{
Property = Button.IsMouseOverProperty,
Value = true
};
trigger.Setters.Add(new Setter()
{
Property = Button.BackgroundProperty,
Value = Brushes.Green
});
trigger.Setters.Add(new Setter()
{
Property = TextBlock.ForegroundProperty,
Value = Brushes.White
});
template.Triggers.Add(trigger);
Setter setter = new Setter()
{
Property = Button.TemplateProperty,
Value = template
};
style.Setters.Add(setter);
Style = style;
}
I was looking on how to replicate a Google button's shadow on hover effect in my WPF document and came across this: https://stackoverflow.com/a/53031057/12299798, which answers my problem exactly, but the only issue I have is that I normally do all the frontend things in XAML and so I am not quite experienced in the styling in C#. My problem is that I want to 'Bind' my foreground colour to the shadow colour, this is because in the code it only sets it to a greyish colour which would mean I would have to do a whole other class for each colour that I want to set it for. Here is my code now:
MainWindow.xaml:
<Button Content="shadow">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="local:UI.Elevation" Value="10"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="local:UI.Elevation" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
UI.cs:
public static class UI
{
public static readonly DependencyProperty ElevationProperty = DependencyProperty.RegisterAttached("Elevation", typeof(double), typeof(UI), new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender, null, OnRedElevationChanged));
public static double GetElevation(this UIElement element) => element.GetValue(ElevationProperty) as double? ?? default;
public static void SetElevation(this UIElement element, double elevation) => element.SetValue(ElevationProperty, elevation);
private static object OnRedElevationChanged(DependencyObject d, object value)
{
if (d is UIElement element && value is double elevation)
if (elevation == 0)
element.Effect = null;
else
{
Effect e = CreateElevation(elevation, element.Effect);
if (e != null)
element.Effect = e;
}
return value;
}
private static Effect CreateElevation(double elevation, Effect source)
{
void MixShadows(DropShadowEffect nearest, DropShadowEffect matched, double balance)
{
matched.BlurRadius = matched.BlurRadius * (1 - balance) + nearest.BlurRadius * balance;
matched.ShadowDepth = matched.ShadowDepth * (1 - balance) + nearest.ShadowDepth * balance;
}
DropShadowEffect[] shadows = new DropShadowEffect[]
{
new DropShadowEffect()
{
BlurRadius = 5,
ShadowDepth = 1
},
new DropShadowEffect()
{
BlurRadius = 8,
ShadowDepth = 1.5
},
new DropShadowEffect()
{
BlurRadius = 14,
ShadowDepth = 4.5
},
new DropShadowEffect()
{
BlurRadius = 25,
ShadowDepth = 8
},
new DropShadowEffect()
{
BlurRadius = 35,
ShadowDepth = 13
}
};
elevation = Math.Max(0, elevation / 12 * shadows.Length - 1);
int prevIndex = (int)Math.Floor(elevation), index = (int)elevation, nextIndex = (int)Math.Ceiling(elevation);
double approx = elevation - index;
DropShadowEffect shadow = shadows[index];
if (approx != 0)
MixShadows(approx < 0 ? shadows[prevIndex] : shadows[nextIndex], shadow, Math.Abs(approx));
bool modify = false;
if (source is DropShadowEffect sourceShadow)
{
sourceShadow.BlurRadius = shadow.BlurRadius;
sourceShadow.ShadowDepth = shadow.ShadowDepth;
shadow = sourceShadow;
modify = true;
}
shadow.Direction = 270;
shadow.Color = Colors.Red;
shadow.Opacity = .42;
shadow.RenderingBias = RenderingBias.Performance;
return modify ? null : shadow;
}
}
And to get what I want in XAML I would do something like this:
<Button Content="shadow">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="local:UI.Elevation" Value="10"/>
<Setter Property="local:UI.Elevation.Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="local:UI.Elevation" Value="0"/>
<Setter Property="local:UI.Elevation.Foreground" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
But I don't quite know how to link up the local:UI.Elevation.Foreground to change the shadow colour, I would have to change this line here from UI.cs: shadow.Color = Colors.Red; and bind it to the Elevation.Foreground, but I am having difficulties. Can anyone help?
All you need to do is updated the UI class with a new attached property named something like Color of type Color and give it a default color you like.
Then, in its OnColorChanged handler, call OnElevationChanged so that the shadow gets recreated.
#region Color (Attached Property)
public static readonly DependencyProperty ColorProperty =
DependencyProperty.RegisterAttached(
"Color",
typeof(Color),
typeof(UI),
new PropertyMetadata(Colors.Black, OnColorChanged));
public static Color GetColor(DependencyObject obj)
{
return (Color)obj.GetValue(ColorProperty);
}
public static void SetColor(DependencyObject obj, Color value)
{
obj.SetValue(ColorProperty, value);
}
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
OnElevationChanged(d, GetElevation((UIElement)d));
}
#endregion
In the CreateElevation method, add a new parameter for the color, so you can set it.
private static Effect CreateElevation(double elevation, Effect source, Color color)
{
...
shadow.Color = color;
...
}
Finally, in OnElevationChanged update it so it to call GetColor so it can pass in the new Color property.
private static object OnElevationChanged(DependencyObject d, object value)
{
if (d is UIElement element && value is double elevation)
if (elevation == 0)
element.Effect = null;
else
{
Effect e = CreateElevation(elevation, element.Effect, GetColor(element));
if (e != null)
element.Effect = e;
}
return value;
}
Example XAML
<Button
Width="100"
Height="20"
Content="shadow">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="local:UI.Elevation" Value="10" />
<Setter Property="local:UI.Color" Value="Blue" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="local:UI.Elevation" Value="5" />
<Setter Property="local:UI.Color" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
NOTE: There is some refactoring you could do to make all this cleaner, but hopefully this will get your started.
As you can see below, there are white margins from both sides, how to expand the gray background horizontally, so it will cover the TextBox from edge to edge?
<RichTextBox x:Name="logTextBox" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Auto" IsReadOnly="True" FontSize="12" Margin="10,165,10,10" >
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="ScrollViewer">
<Setter Property="MaxWidth" Value="480" />
</Style>
</RichTextBox.Resources>
</RichTextBox>
Usage:
public void AddLog(string log)
{
Run run = new Run(log);
Paragraph paragraph = new Paragraph(run);
paragraph.Background = new SolidColorBrush(Colors.Gray);
var numberOfBlocks = logTextBox.Document.Blocks.Count;
const int MaxNumberOfBlocks = 100;
if (numberOfBlocks > MaxNumberOfBlocks)
{
logTextBox.Document.Blocks.Remove(logTextBox.Document.Blocks.FirstBlock);
}
logTextBox.Document.Blocks.Add(paragraph);
logTextBox.ScrollToEnd();
}
Set the PagePadding property of the FlowDocument to 0 after the RichTextBox has been loaded:
public void AddLog(string log)
{
Run run = new Run(log);
Paragraph paragraph = new Paragraph(run);
paragraph.Background = new SolidColorBrush(Colors.Gray);
var numberOfBlocks = logTextBox.Document.Blocks.Count;
const int MaxNumberOfBlocks = 100;
if (numberOfBlocks > MaxNumberOfBlocks)
{
logTextBox.Document.Blocks.Remove(logTextBox.Document.Blocks.FirstBlock);
}
logTextBox.Document.Blocks.Add(paragraph);
logTextBox.Document.PagePadding = new Thickness(0); //<--
logTextBox.ScrollToEnd();
}
I copied unit based textbox class from internet and class definition looks as follow:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SapQmWp.Classes
{
public class UnitTextBox : TextBox
{
public static DependencyProperty UnitTextProperty =
DependencyProperty.Register(
"UnitText",
typeof(string),
typeof(UnitTextBox),
new FrameworkPropertyMetadata(
default(string),
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender));
public static DependencyProperty UnitPaddingProperty =
DependencyProperty.Register(
"UnitPadding",
typeof(Thickness),
typeof(UnitTextBox),
new FrameworkPropertyMetadata(
new Thickness(5d, 0d, 0d, 0d),
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender));
public static DependencyProperty TextBoxWidthProperty =
DependencyProperty.Register(
"TextBoxWidth",
typeof(double),
typeof(UnitTextBox),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.AffectsMeasure));
private FormattedText _unitText;
private Rect _unitTextBounds;
public string UnitText
{
get { return (string) GetValue(UnitTextProperty); }
set { SetValue(UnitTextProperty, value); }
}
public Thickness UnitPadding
{
get { return (Thickness) GetValue(UnitPaddingProperty); }
set { SetValue(UnitPaddingProperty, value); }
}
public double TextBoxWidth
{
get { return (double) GetValue(TextBoxWidthProperty); }
set { SetValue(TextBoxWidthProperty, value); }
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == ForegroundProperty)
EnsureUnitText(true);
}
protected override Size MeasureOverride(Size constraint)
{
var textBoxWidth = TextBoxWidth;
var unit = EnsureUnitText(true);
var padding = UnitPadding;
if (unit != null)
{
var unitWidth = unit.Width + padding.Left + padding.Right;
var unitHeight = unit.Height + padding.Top + padding.Bottom;
constraint = new Size(
constraint.Width - unitWidth,
Math.Max(constraint.Height, unitHeight));
}
var hasFixedTextBoxWidth = !double.IsNaN(textBoxWidth) &&
!double.IsInfinity(textBoxWidth);
if (hasFixedTextBoxWidth)
constraint = new Size(textBoxWidth, constraint.Height);
var baseSize = base.MeasureOverride(constraint);
var baseWidth = hasFixedTextBoxWidth ? textBoxWidth : baseSize.Width;
if (unit != null)
{
var unitWidth = unit.Width + padding.Left + padding.Right;
var unitHeight = unit.Height + padding.Top + padding.Bottom;
return new Size(
baseWidth + unitWidth,
Math.Max(baseSize.Height, unitHeight));
}
return new Size(baseWidth, baseSize.Height);
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
var textSize = arrangeBounds;
var unit = EnsureUnitText(false);
var padding = UnitPadding;
if (unit != null)
{
var unitWidth = unit.Width + padding.Left + padding.Right;
var unitHeight = unit.Height + padding.Top + padding.Bottom;
textSize.Width -= unitWidth;
_unitTextBounds = new Rect(
textSize.Width + padding.Left,
(arrangeBounds.Height - unitHeight)/2 + padding.Top,
textSize.Width,
textSize.Height);
}
var baseSize = base.ArrangeOverride(textSize);
if (unit != null)
{
var unitWidth = unit.Width + padding.Left + padding.Right;
var unitHeight = unit.Height + padding.Top + padding.Bottom;
return new Size(
baseSize.Width + unitWidth,
Math.Max(baseSize.Height, unitHeight));
}
return baseSize;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var unitText = EnsureUnitText(false);
if (unitText != null)
drawingContext.DrawText(unitText, _unitTextBounds.Location);
}
private FormattedText EnsureUnitText(bool invalidate = false)
{
if (invalidate)
_unitText = null;
if (_unitText != null)
return _unitText;
var unit = UnitText;
if (!string.IsNullOrEmpty(unit))
{
_unitText = new FormattedText(
unit,
CultureInfo.InvariantCulture,
FlowDirection,
new Typeface(
FontFamily,
FontStyle,
FontWeight,
FontStretch),
FontSize,
Foreground);
}
return _unitText;
}
}
}
And I insert into the window xaml as follow:
...
<ui:UnitTextBox Grid.Row="1" Style="{StaticResource WeightTbStyle}" UnitText="KG" Text="{Binding TargetWeight, UpdateSourceTrigger=PropertyChanged}"/>
...
But the background color is null rather than a color.
My question, how to set background color for UnitText too?
Update
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SapQmWp.Themes">
<Style x:Key="WeightTbStyle" TargetType="TextBox">
<Setter Property="Background" Value="#F8F8F8" />
<Setter Property="BorderBrush" Value="{x:Null}"/>
<Setter Property="Margin" Value="10,40,10,40" />
<Setter Property="Padding" Value="0,0,0,5" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Foreground" Value="#404242"/>
<Setter Property="FontSize" Value="24pt"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="BorderThickness" Value="0" />
</Style>
</ResourceDictionary>
Update 2
When I set the background on controls like
<ui:UnitTextBox Grid.Row="1" Background="Tomato" Style="{StaticResource WeightTbStyle}" UnitText="KG" Text="{Binding TargetWeight, UpdateSourceTrigger=PropertyChanged}"/>
as result I've got:
In your
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var unitText = EnsureUnitText(false);
if (unitText != null)
drawingContext.DrawText(unitText, _unitTextBounds.Location);
}
Try something like this to set color and formatting
var formattedText = new FormattedText(unitText,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(new FontFamily("ANY_FONT_FAMILY"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal),
24, Brushes.Red);
drawingContext.DrawText(formattedText, _unitTextBounds.Location);
}
And for background color just draw a rectangle before you render your text.
drawingContext.DrawRectangle(Brushes.Red, Nothing, YOUR_TEXT_RECT);
I want to hide a grid view column when a menu-item is clicked.
Xaml:
<MenuItem Header="View">
<MenuItem Header="Columns" Name="menuView" ItemsSource="{Binding Path= MyMenuItems}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="IsChecked" Value="{Binding IsChecked,Mode=TwoWay}"/>
<Setter Property="IsCheckable" Value="{Binding IsCheckable}"/>
<Setter Property="Icon" Value= "{Binding ImageIcon}"/>
<Setter Property="IsEnabled" Value= "{Binding Path =IsEnabled}"/>
<Setter Property="ItemsSource" Value="{Binding Path= MyMenuItems}"/>
<Setter Property= "Command" Value= "{Binding DataContext.CheckedViewMenuItemsCmd, RelativeSource ={RelativeSource AncestorType={x:Type MenuItem}}}"/>
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="StaysOpenOnClick" Value="{Binding StaysOpenOnClick}"/>
</Style> </MenuItem.ItemContainerStyle>
</MenuItem>
<ListView Name="MyListView" Grid.Row="1" SelectedItem ="{Binding SelectedFeature}" ItemsSource="{Binding Path= MyListItems}">
<ListView.View>
<GridView util:GridViewColumnClass.HeaderTextMember="HeaderText"
util:GridViewColumnClass.DisplayMemberMember="DisplayMember"
util:GridViewColumnClass.ColumnsSource=" {Binding Columns}"/>
</ListView.View>
The menu items header is the same as Grid view column header . When a menu item is checked or unchecked, I would like the grid view column to be shown or hidden.
I do have a GidViewColumnVisibilityManager similar to this one here .
WPF: How to hide GridViewColumn using XAML?
ViewModel:
public MyViewModel()
{
MyListItems = SomeClass.instance.messages;
_MenuItems = new ObservableCollection<MenuClass>();
//populates the view menu
PopulateViewMenu();
this.Columns = new ObservableCollection<ColumnDescriptor>
{
new ColumnDescriptor{ Width = 40, HeaderText ="column1", DisplayMember= "column1"},
new ColumnDescriptor{ Width = 40, HeaderText="column2" , DisplayMember= "column2"},
new ColumnDescriptor{ Width = 70, HeaderText="column3" , DisplayMember="column3"},
};
//event to command
CheckedViewMenuItemsCmd = new RelayCommand<MenuClass>(CheckedViewMenuItems);
}
public ObservableCollection<ColumnDescriptor> Columns { get; private set; }
private ICommand _addColumnCommand;
public ICommand AddColumnCommand
{
get
{
if (_addColumnCommand == null)
{
_addColumnCommand = new RelayCommand<string>(
s =>
{
this.Columns.Add(new ColumnDescriptor { HeaderText = s, DisplayMember = s });
});
}
return _addColumnCommand;
}
}
private ICommand _removeColumnCommand;
public ICommand RemoveColumnCommand
{
get
{
if (_removeColumnCommand == null)
{
_removeColumnCommand = new RelayCommand<string>(
s =>
{
this.Columns.Remove(this.Columns.FirstOrDefault(d => d.DisplayMember == s));
});
}
return _removeColumnCommand;
}
}
private void CheckedViewMenuItems(MenuClass m)
{
try
{
bool IsChecked = m.IsChecked;
if (IsChecked)
{
ColumnDescriptor cl1 = new ColumnDescriptor{ Width = 40, HeaderText =m.Header, DisplayMember= "Revision"};
int idx = Convert.ToInt32(m.Tag);
int insertidx = Math.Min(idx, this.Columns.Count);
this.Columns.Insert(insertidx, cl1);
}
else
{
foreach (var item in this.Columns)
{
if (item.HeaderText == m.Header)
{
// item.DisplayMember = "";
this.Columns.Remove(item);
break;
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(String.Format("{0}{1}{2}{1}{3}", ex.GetType().ToString(), Environment.NewLine, ex.Message, ex.StackTrace));
}
}
I am a bit confused about how to use the booleon to visible converter and implement the functionality. Your help is appreciated. Please ask me questions if you have any. Thank you folks.
I tried insert and remove instead of show and hide, the Remove works like a charm but the insert needs a display member binding to bind to the collection every time which is obvious. I tried for one column, it works fine, i need it to work for all. Any thoughts?
I found a workaround. I have a new function GetdisplayMember which returns a string, depending on which menuitem was clicked. Here is workaround. Works nicely. Cheers!
private string GetDisplayMember(int tag)
{
string displayMember = string.Empty;
if (tag != null)
{
switch (tag)
{
case 0:
displayMember = "Revision";
break;
case 1:
displayMember = "Class";
break;
case 2:
displayMember = "Errordate";
break;
}
}
return displayMember;
}
private void CheckedViewMenuItems(MenuClass m)
{
try
{
if (m!=null)
{
bool IsChecked = m.IsChecked;
if (IsChecked)
{
ColumnDescriptor cl1 =new ColumnDescriptor
{
HeaderText =m.Header,
DisplayMember= GetDisplayMember(m.Tag)
};
int idx = Convert.ToInt32(m.Tag);
int insertidx = Math.Min(idx,
this.Columns.Count);
this.Columns.Insert(insertidx, cl1);
}
else
{
foreach (var item in this.Columns)
{
if (item.HeaderText == m.Header)
{
// item.DisplayMember = "";
this.Columns.Remove(item);
break;
}
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(String.Format("{0}{1}{2}{1}{3}", ex.GetType().ToString(), Environment.NewLine, ex.Message, ex.StackTrace));
ErrorLogger.Log(LogLevel.Error, ex.ToString());
}