Convert IEnumerable<Color> into a brush - wpf

I have an IEnumerable of Color which I want to use as the basis for a brush.
At the moment, I convert the IEnumerable into a Bitmap, into a bitmapsource, into an imagebrush, but this is a bit slow, is there any brush class which can do what I want in a faster way?
edit, What I want to do: Use the brush in a pen to draw a line in a drawing visual, where the IEnumerable of Color is used as line color. If I have a collection of { Colours.Green, Colours.Red}, I want the resulting line to be half green, half red.

Here's a method that would convert your IEnumerable to a LinearGradientBrush.
There are 2 gradient stops for each color in order to create a hard transition between colours rather than a gradient.
LinearGradientBrush CreateBrush(IEnumerable<Color> colors) {
var colorArray = colors.ToArray();
var step = 1.0 / colorArray.Length;
var gradientStops = new GradientStopCollection();
for (int i = 0; i < colorArray.Length; i++) {
var color = colorArray[i];
gradientStops.Add(new GradientStop(color, i * step));
gradientStops.Add(new GradientStop(color, (i + 1) * step));
}
return new LinearGradientBrush(gradientStops);
}

Related

Color Picker Combo Box

I want to customize my combobox each item, same as font, background, etc.
Is there any simple way to set custom background color of each Combobox item?
This is simple example to do that. In this example my combobox has some item same as color name (Red, Blue, etc) and change the background of each item from this. Just flow the steps:
1) Set the DrawMode to OwnerDrawVariable:
If this property set to Normal this control never raise DrawItem event
ComboBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
2) Add an DrawItem event:
ComboBox1.DrawItem += new DrawItemEventHandler(ComboBox1_DrawItem);
3) Entry your own code to customize each item:
private void ComboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rect = e.Bounds; //Rectangle of item
if (e.Index >= 0)
{
//Get item color name
string itemName = ((ComboBox)sender).Items[e.Index].ToString();
//Get instance a font to draw item name with this style
Font itemFont = new Font("Arial", 9, FontStyle.Regular);
//Get instance color from item name
Color itemColor = Color.FromName(itemName);
//Get instance brush with Solid style to draw background
Brush brush = new SolidBrush(itemColor);
//Draw the item name
g.DrawString(itemName, itemFont, Brushes.Black, rect.X, rect.Top);
//Draw the background with my brush style and rectangle of item
g.FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height);
}
}
4) Colors need to be added as well:
ComboBox1.Items.Add("Black");
ComboBox1.Items.Add("Blue");
ComboBox1.Items.Add("Lime");
ComboBox1.Items.Add("Cyan");
ComboBox1.Items.Add("Red");
ComboBox1.Items.Add("Fuchsia");
ComboBox1.Items.Add("Yellow");
ComboBox1.Items.Add("White");
ComboBox1.Items.Add("Navy");
ComboBox1.Items.Add("Green");
ComboBox1.Items.Add("Teal");
ComboBox1.Items.Add("Maroon");
ComboBox1.Items.Add("Purple");
ComboBox1.Items.Add("Olive");
ComboBox1.Items.Add("Gray");
You can change the rectangle size and position to draw your own item as you want.
I hope this post is useful.

WPF - How to work out how much of a canvas background image is cropped?

I have a canvas with a background image:
var bi = new BitmapImage(new Uri(imgLocFull));
var ib = new ImageBrush(bi) {Stretch = Stretch.UniformToFill};
MyCanvas.Background = ib;
I am overlaying various shapes on the image, and want the position of the shapes relative to the background image to be fixed.
If my application window is resized, the amount of the image that is cropped, horizontally and vertically, changes, and when my shapes are redrawn, they do not appear in the same position on the background image.
How can I determine how much of the image has been cropped (to apply an adjustment factor to the overlaid objects' positions?) Or is there a better way of fixing the location of a shape relative to the background image?
Here is my present drawing code:
var l = new Ellipse();
var scb = new SolidColorBrush();
scb.Color = Color.FromRgb(rCol, gCol, bCol);
l.Fill = scb;
l.StrokeThickness = 0;
l.Width = 3;
l.Height = 3;
Canvas.SetBottom(l, point.Y); // * clipping factor here?
Canvas.SetLeft(l, point.X); // * clipping factor here?
MyCanvas.Children.Add(l);
EDIT: Further Clarification
Here's a concrete example of what I am trying to achieve. My image is an aerial photograph, and I want to mark a particular geographical feature (with, say, an ellipse.)
When the window is resized, the ellipse doesn't stay on the feature, it stays relative to the left and top of the canvas.
I can get it closer to the right place by moving it using a factor (newx = newheight/oldheight * oldx) but this doesn't quite work because of the UniformToFill stretch mode, which sees some of the image clipped off the canvas.
The Top and Left of the Canvas are 'anchored', while the Bottom and Right move when resizing... try setting the Canvas.Top Attached Property instead, along with the Canvas.Left Attached Property as you are:
var l = new Ellipse();
var scb = new SolidColorBrush();
scb.Color = Color.FromRgb(rCol, gCol, bCol);
l.Fill = scb;
l.StrokeThickness = 0;
l.Width = 3;
l.Height = 3;
Canvas.SetTop(l, point.Y); // * clipping factor here?
Canvas.SetLeft(l, point.X); // * clipping factor here?
MyCanvas.Children.Add(l);
UPDATE >>>
You asked Or is there a better way of fixing the location of a shape relative to the background image?
I answered this question, so I don't understand why you would need to do anything else... your objects will not move when the screen in resized *if you only set the Grid.Top and Grid.Left properties.

unify elements size in stackpanel

I need help in unifying size of elements in stackpanel
void MainPageLoaded(object sender, RoutedEventArgs e)
{
var random = new Random();
for (var i = 0; i < 5; i++)
{
var grid = new Grid();
var border = new Border()
{
Height = random.Next(50, 150),
Width = random.Next(50, 150),
Margin = new Thickness(10),
BorderBrush = new SolidColorBrush(Colors.White),
BorderThickness = new Thickness(1)
};
grid.Children.Add(border);
imageBoxesStackPanel.Children.Add(grid);
}
var h = imageBoxesStackPanel.Children.Max(n => n.DesiredSize.Height);
what I am trying to achieve is to find max height and max width of each grid in stackpanel and apply it to all of them. The problem is that desired size is always wrong.
You can only do this in a custom way after a measure/arrange pass, before that the sizes won't be visible.
After that (in the OnLoaded event, which you have), you can use the ActualHeight and ActualWidth of the grids.
In short:
var h = imageBoxesStackPanel.Children.Max(n => n.ActualHeight);
This is however bad for performance and will trigger another layout pass.
Remarks:
In WPF the best solution would be a SharedSizeGroup or a UniformGrid. This is not implemented in Silverlight, but there are people who have implemented it.
In WPF there is the UniformGrid to do this job, but unfortunately it's not implemented for Silverlight by default. There are several alternatives for it, e.g. this one

Flip Horizontically Grid Background Image(Brush)

I have set a Grid's background brush as an ImageBrush.
But when I set the Grid's FlowDirection to RightToLeft, the image is flipped horizontically.
Is it possible to (un)flip the grid background ImageBrush using a certain Transition or any other way?
Not much you can do about that with sensible means (there same means that are far from sensible).
Instead place an Image element as the first item in the Grid with Grid.RowSpan, Grid.ColumnSpan to cover all the cells. Use Stretch="Fill" on the Image since thats how a background typically behaves.
Well, i do understand that my comment is outdated, but this question is popping up one of the first in Google search, so here is my solution:
I was localizing the application for the right-to-left culture. The simple decision to set FlowDirection=RTL comes with unexpected drawbacks like the background containing the company logo is flipped. I have applied the matrix transformation for the image brush used to render the background:
var mbgBrush = TryFindResource("MainBackground") as Brush;
if (mbgBrush == null) return null;
if (FlowDirection == FlowDirection.LeftToRight) return mbgBrush;
var mainBgImageBrush = mbgBrush as ImageBrush;
if (mainBgImageBrush == null) return mbgBrush;
var flipXaxis = new MatrixTransform(-1.0, 0, 0, 1.0, 1, 0);
var flippedBrush = new ImageBrush
{
Stretch = Stretch.None,
Opacity = 1.0,
ImageSource = mainBgImageBrush.ImageSource,
RelativeTransform = flipXaxis
};
return flippedBrush;

How to format specific text in WPF?

I have this code that adds dotted lines under text in text box:
// Create an underline text decoration. Default is underline.
TextDecoration myUnderline = new TextDecoration();
// Create a linear gradient pen for the text decoration.
Pen myPen = new Pen();
myPen.Brush = new LinearGradientBrush(Colors.White, Colors.White, new Point(0, 0.5), new Point(1, 0.5));
myPen.Brush.Opacity = 0.5;
myPen.Thickness = 1.0;
myPen.DashStyle = DashStyles.Dash;
myUnderline.Pen = myPen;
myUnderline.PenThicknessUnit = TextDecorationUnit.FontRecommended;
// Set the underline decoration to a TextDecorationCollection and add it to the text block.
TextDecorationCollection myCollection = new TextDecorationCollection();
myCollection.Add(myUnderline);
PasswordSendMessage.TextDecorations = myCollection;
My problem is I need only the last 6 characters in the text to be formatted!
Any idea how can I achieve that?
Instead of setting the property on the entire TextBlock, create a TextRange for the last six characters and apply the formatting to that:
var end = PasswordSendMessage.ContentEnd;
var start = end.GetPositionAtOffset(-6) ?? PasswordSendMessage.ContentStart;
var range = new TextRange(start, end);
range.ApplyPropertyValue(Inline.TextDecorationsProperty, myCollection);
If PasswordSendMessage is a TextBox rather than a TextBlock, then you cannot use rich text like this. You can use a RichTextBox, in which case this technique will work but you will need to use PasswordSendMessage.Document.ContentEnd and PasswordSendMessage.Document.ContentStart instead of PasswordSendMessage.ContentEnd and PasswordSendMessage.ContentStart.
You could databind your text to the Inlines property of TextBox and make a converter to build the run collection with a seperate Run for the last 6 characters applying your decorations

Resources