So here it goes, I making a c# project in wpf and im stuck at trying to make a text box with a line that separates text.
At the moment i got the textbox like this:
Instead of using "-------" i want to make a predefined line in the textbox. Is this possible ?
Agg. The textbox is editable in runtime
The drawn line should have the properties:
It should not be edit able !
It shouldn't only be an empty line
It should be a visible line that has the width line.width = box.width !
If you don't need a full textbox implementation then this might help. It's got real issues with not showing the cursor ect but might give you a start.
first add the following converter to your project.
public class TextLineConverter : MarkupExtension, IValueConverter
{
static TextLineConverter converter;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string[] results = null;
string newText = value as string;
if (newText != null)
{
results = newText.Split('\r');
if (results.Length > 0)
for (int i = 0; i < results.Length; i++)
if (results[i].Length > 0)
if (results[i][0] == '\n')
results[i] = results[i].Substring(1, results[i].Length - 1);
}
return results;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (converter == null) converter = new TextLineConverter();
return converter;
}
public TextLineConverter()
{
}
}
And the following style.
<Style TargetType="TextBox">
<Style.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,2" BorderBrush="Black" >
<ContentPresenter Content="{Binding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<ListView Focusable="False" ItemsSource="{Binding Text, Converter={local:TextLineConverter}, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
On rereading your question, i believe that the best way to do what you want is to create your own user-control that has an attribute specifically for this. Do some research on C# User Controls and perhaps you'll find out how to do this.
Related
I am using a datagrid and changing the color of the rows according to their conditions and I am performing this programmatically.
follow the example
as my datagrid is bound to a datatable I load information straight from the datatable
private void UpdateCor () {
gvDados.UpdateLayout ();
for (int i = 0; i <dt.Rows.Count; i ++)
{
var rowContext = (DataGridRow)
gvDados.ItemContainerGenerator.ContainerFromIndex (i);
if (rowContext! = null)
{
if (dt.Rows [i] ["situation"]. ToString (). Equals (1))
rowContext.Background = Brushes.Green;
else
rowContext.Background = Brushes.Red;
}
}
}
With this I can update the color of my grid even though it is not the best method to be approached. my problem is this, whenever I use the scroll to go down or up the bar the colors become outdated. How do I prevent this from happening? that even when I roll the bar the colors stay fixed?
This is a similar question to this question.
Can be done using datatrigger:
<DataGrid ItemsSource="{Binding YourItemsSource}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="State1">
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="State2">
<Setter Property="Background" Value="Green"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Often, XAML is too simple to express more complicated conditions. I prefer to put the logic which values should use which colors into a converter. This leads to a simpler XAML and much greater flexibility for the converter in C#.
<datagrid.rowstyle>
<style targettype="DataGridRow">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self},
Path=Item.situation, Converter={StaticResource ValueToBackgroundConverter}}"/>
</style>
</datagrid.rowstyle>
In C#:
class ValueToBackgroundConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int) {
int quantity = (int)value;
if (quantity>=100) return Brushes.White;
if (quantity>=10) return Brushes.WhiteSmoke;
if (quantity>=0) return Brushes.LightGray;
return Brushes.White; //quantity should not be below 0
}
//value is not an integer. Do not throw an exception
// in the converter, but return something that is obviously wrong
return Brushes.Yellow;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
Formatting the various parts of a WPF Datagrid is notoriously difficult and Microsoft is not providing the necessary information how to do it. Read my article Codeproject: Guide to WPF DataGrid formatting using binding to get a better understanding how to do it easily.
I'm having trouble with finding the solution for the problem, namely I had an idea to color each row/column of combobox with different colors, depending on the area, but i cannot find any clues or hints or instructions to do so. the app is pretty simple
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="84,70,0,0" VerticalAlignment="Top" Width="230"/>
this is my XAML combobox, which i fill from the code:
SortedList<int, string> AreaList = new SortedList<int, string>();
AreaList.Add(1, "Agriculture");
AreaList.Add(2, "Forestry");
AreaList.Add(3, "Fruits");
AreaList.Add(4, "Food");
AreaList.Add(5, "Metals");
AreaList.Add(6, "Mining");
AreaList.Add(7, "Electricity");
AreaList.Add(8, "Building Contracts");
AreaList.Add(9, "Transport");
AreaList.Add(10, "Alcohol");
AreaList.Add(11, "Information Technologies");
AreaList.Add(12, "Health And Social Services");
AreaList.Add(13, "Art and Entertainement");
AreaList.Add(14, "Hospitality Business");
AreaList.Add(15, "Education");
AreaList.Add(16, "Real Estate");
AreaList.Add(17, "Sales");
AreaList.Add(18, "Architecture");
AreaList.Add(19, "Engineering");
AreaList.Add(20, "Wholesale");
AreaList.Add(21, "Other");
comboBox1.ItemsSource = AreaList.ToList();
comboBox1.SelectedValuePath = "Key";
comboBox1.DisplayMemberPath = "Value";
each of these items have their color in another window, but i would like to show those colors in the combobox, the background of "Agriculture" row/column should be green etc.
Is there a solution to this, or do i have to redo it all over?
You could use an ItemContainerStyle with a DataTrigger for each value that maps to a colour:
<ComboBox x:Name="comboBox1">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Value}" Value="Agriculture">
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Value}" Value="Forestry">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<!-- and so on-->
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
You may also want to read this:
Changing the background colour of a ComboBox in WPF: https://blog.magnusmontin.net/2014/04/30/changing-the-background-colour-of-a-combobox-in-wpf-on-windows-8/
You can make use of ItemContainerStyle and Converter
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (((KeyValuePair<int, string>)value).Value.ToString() == "Agriculture")
return Brushes.Green;
//and so on or other ways to get the color
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and XAML goes as follows,
<Window.Resources>
<local:StringToColorConverter x:Key="StringToColorConverter"/>
</Window.Resources>
<Grid >
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="84,70,0,0" VerticalAlignment="Top" Width="230">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{Binding Converter={StaticResource StringToColorConverter}}">
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
I have an application that uses two separate projects. One is for the main executable which contains my ViewModels and the other is to control the theme/style of the application.
In the theme project, I have customized the DataGridColumnHeader's Style to include a CheckBox. Now how do I databind the CheckBoxes to my ViewModel?
My theme xaml
<Style x:Key='PlottableFilteringColumnHeaderStyle' TargetType='{x:Type primitives:DataGridColumnHeader}'>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type primitives:DataGridColumnHeader}">
<Grid>
<themes:DataGridHeaderBorder x:Name='HeaderBorder'>
<Grid x:Name="GridColumnHeader">
<StackPanel x:Name="argStackPanel">
<CheckBox x:Name="argCheckBox" Content="Enable Arg" Style="{DynamicResource ResourceKey=DefaultCheckBox}" />
</StackPanel>
</Grid>
</themes:DataGridHeaderBorder>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am then using MultiBinding for the argCheckBox
public class HeaderArgConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string headerText = values[0] as string;
if (!String.IsNullOrWhiteSpace(headerText))
{
FrameworkElement targetElement = values[1] as FrameworkElement;
DataGridColumnHeader header = targetElement.TemplatedParent as DataGridColumnHeader;
string columnName = header.DataContext != null ? header.DataContext.ToString() : "";
var argNumber = System.Text.RegularExpressions.Regex.Match(columnName.Split(':')[0], #"\d+$").Value; // use the header text to determine which arg number
Binding binding = new Binding("SelectedViewModel.EnableArg" + argNumber);
binding.Source = Window.DataContextProperty; // This is what I am unsure about
(targetElement as CheckBox).SetBinding(CheckBox.IsCheckedProperty, binding);
}
}
}
I keep getting 'BindingExpression path error: property not found on 'object'' error. Any ideas on how to fix this or if there is a better way to do this?
At the moment I have a date time axis where the date is in-line with the points, is there anyway to get this date to appear in the center such as on a bar chart.
<Style x:Key="DateTimeAxisLabelStyle2" TargetType="chartingToolkit:DateTimeAxisLabel">
<Setter Property="DaysIntervalStringFormat" Value="{}{0:dd-MMM}" />
<Setter Property="HoursIntervalStringFormat" Value="{}{0:hh:mm tt}" />
<!--<Setter Property="RenderTransformOrigin" Value="1,0.5" />
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="-45" />
</Setter.Value>
</Setter>-->
<!--<Setter Property="Margin" Value="30,0,-10,0" />-->
</Style>
<chartingToolkit:DateTimeAxis IntervalType="Days"
Interval="1"
Minimum="{Binding StartDate}"
Maximum="{Binding EndDate}"
Orientation="X"
VerticalContentAlignment="Center"
Title="Day"
AxisLabelStyle="{StaticResource DateTimeAxisLabelStyle2}" />
Any help would be greatly appreciated.
Here's what i got:
XAML:
<Window.Resources>
<Style x:Key="DateTimeAxisLabelStyle1" TargetType="{x:Type chartingToolkit:DateTimeAxisLabel}">
<Setter Property="DaysIntervalStringFormat" Value="{}{0:dd-MMM}"></Setter>
<Setter Property="RenderTransformOrigin" Value="0.80,0.20"></Setter>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="-90"></RotateTransform>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<chartingToolkit:Chart Margin="0" Title="Chart Title">
<chartingToolkit:Chart.DataContext>
<local:MyDataCollection/>
</chartingToolkit:Chart.DataContext>
<chartingToolkit:Chart.Axes>
<chartingToolkit:DateTimeAxis Minimum="{Binding StartDate}" Maximum="{Binding EndDate}" Orientation="X" ShowGridLines="True" AxisLabelStyle="{DynamicResource DateTimeAxisLabelStyle1}"/>
</chartingToolkit:Chart.Axes>
<chartingToolkit:LineSeries DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding}"/>
</chartingToolkit:Chart>
</Grid>
Chart:
Here's what I did using the WPF Toolkit Source for reference.
I created a custom class deriving from DateTimeAxis, then overrode the "GetPlotAreaCoordinate" method. The DateTimeAxis.Render() calls that method three times with the same list of "DateTime" values, once for the MajorTickmarks, once for MinorTickmarks, and once for the date label. There were no minor tickmarks in the list, so the method was actually only getting called twice. I just keep a list of the values that have been evaluated and assume that if it's in the list it's already done the tickmarks and is now doing the Labels.
class CustomDateTimeAxis : DateTimeAxis
{
List<object> _valueList = new List<object>();
UnitValue prevBaseValue;
protected override UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length)
{
_valueList.Add(value);
UnitValue baseValue = base.GetPlotAreaCoordinate(value, currentRange, length);
int valueCount = _valueList.Count((x) => x.Equals(value));
if (valueCount == 2)
return new UnitValue(baseValue.Value + 27, baseValue.Unit);
prevBaseValue = baseValue;
return baseValue;
}
protected override void Render(Size availableSize)
{
base.Render(availableSize);
_valueList.Clear();
}
}
"27" is just a number I was trying out. You might want to play with that to see what works best for you.
return new UnitValue(baseValue.Value + 27, baseValue.Unit);
I created a Margin-Converter:
public class MarginConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var dateTimeAxis = values[0] as DateTimeAxis; ;
var actualAxisLength = values[1] as double?;
var actualMaximum = values[2] as DateTime?;
var actualMinimum = values[3] as DateTime?;
if (dateTimeAxis == null ||
!dateTimeAxis.Interval.HasValue ||
!actualAxisLength.HasValue ||
!actualMaximum.HasValue ||
!actualMinimum.HasValue)
return null;
double xMargin = 0;
var interval = dateTimeAxis.Interval.Value;
var timeSpan = actualMaximum.Value - actualMinimum.Value;
var timeSpanInDays = timeSpan.TotalDays;
if (dateTimeAxis.IntervalType == DateTimeIntervalType.Months)
{
xMargin = 30 * interval * actualAxisLength.Value / timeSpanInDays;
}
else if (dateTimeAxis.IntervalType == DateTimeIntervalType.Days)
{
xMargin = interval * actualAxisLength.Value / timeSpanInDays;
}
return new Thickness(xMargin, 10, 0, -30);
}
public object[] ConvertBack(object value, System.Type[] targetType, object parameter, CultureInfo culture)
{
return null;
}
}
called the X-Axis 'SharedXAxis' and used the converter like this:
<Setter Property="Margin">
<Setter.Value>
<MultiBinding Converter="{StaticResource MarginConv}">
<Binding ElementName="SharedXAxis"/>
<Binding ElementName="SharedXAxis" Path="ActualWidth"/>
<Binding ElementName="SharedXAxis" Path="ActualMaximum"/>
<Binding ElementName="SharedXAxis" Path="ActualMinimum"/>
</MultiBinding>
</Setter.Value>
Imo this should be full dynamic.
The Top- and Bottom-Values of the Thickness of the Margin-Converter ('10' and '-30' in my case) as well as the Bottom-Value of the Padding of the Chart itself have to be adjusted, I don't know why.
I have one TextBlock having width say 100. When the text length is a large one I want to show the characters that is accomodated in that textblock and a (...) button besides the text to specify user that more text is also there. Upon click on that (...) button, the full text will be shown in a separate pop up window.
So i want how the dynamic (...) button will be shown whenever the text length exceed the size of the textblock. Please answer
This isn't exactly what you want, but it's a similar idea and just uses the baked-in stuff:
<TextBlock MaxWidth="200"
Text="{Binding YourLongText}"
TextTrimming="WordEllipsis"
ToolTip="{Binding YourLongText}" />
So you have a TextBlock with a maximum width, and when the text can't fit it displays an ellipsis ("..."). Hovering over the TextBlock with your mouse will show the full text in a ToolTip.
Just experience the same requirement for adding ellipsis on button so adding the solution here
<Style x:Key="editButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center" >
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
Notice the resources in content presenter.
I believe what you want is to set the TextTrimming property. Settng it to WordElilipsis or CharacterEllipsis should provide what you need.
My solution to the problem is probably overkill, but allows for some configuration and control.
I created a behavior that allows me to set the character limit for each binding.
internal class EllipsisStringBehavior
{
public static readonly DependencyProperty CharacterLimitDependencyProperty = DependencyProperty.RegisterAttached("CharacterLimit", typeof(int), typeof(EllipsisStringBehavior), new PropertyMetadata(255, null, OnCoerceCharacterLimit));
public static readonly DependencyProperty InputTextDependencyProperty = DependencyProperty.RegisterAttached("InputText", typeof(string), typeof(EllipsisStringBehavior), new PropertyMetadata(string.Empty, OnInputTextChanged));
// Input Text
public static string GetInputText(DependencyObject dependencyObject)
{
return Convert.ToString(dependencyObject.GetValue(InputTextDependencyProperty));
}
public static void SetInputText(DependencyObject dependencyObject, string inputText)
{
dependencyObject.SetValue(InputTextDependencyProperty, inputText);
}
// Character Limit
public static int GetCharacterLimit(DependencyObject dependencyObject)
{
return Convert.ToInt32(dependencyObject.GetValue(CharacterLimitDependencyProperty));
}
public static void SetCharacterLimit(DependencyObject dependencyObject, object characterLimit)
{
dependencyObject.SetValue(CharacterLimitDependencyProperty, characterLimit);
}
private static void OnInputTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textblock = (TextBlock)d;
string input = e.NewValue == null ? string.Empty : e.NewValue.ToString();
int limit = GetCharacterLimit(d);
string result = input;
if (input.Length > limit && input.Length != 0)
{
result = $"{input.Substring(0, limit)}...";
}
textblock.Text = result;
}
private static object OnCoerceCharacterLimit(DependencyObject d, object baseValue)
{
return baseValue;
}
}
I then simply add the using to my user control...
<UserControl
xmlns:behavior="clr-namespace:My_APP.Helper.Behavior"
d:DesignHeight="300" d:DesignWidth="300">
...and apply the behavior to the TextBlock control I wish to use it on.
<TextBlock Margin="0,8,0,8"
behavior:EllipsisStringBehavior.CharacterLimit="10"
behavior:EllipsisStringBehavior.InputText="{Binding Path=DataContext.FeedItemTwo.Body, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignSubheadingTextBlock}"
FontSize="14"/>
Hope this helps.