views:

43

answers:

2

I have a WPF 'monitor widget' that displays CPU, RAM and disk performance as small percentage bars. I use the following gradient as a resource to divide the bars into 4 sections (ie. 0% - 25%, 25% - 50%, etc.)

<LinearGradientBrush x:Key="Quarters" StartPoint="0,0" EndPoint="0,15" MappingMode="Absolute">
    <GradientStop Color="LightGreen" Offset="0.0" />
    <GradientStop Color="LightGreen" Offset="0.24" />
    <GradientStop Color="Yellow" Offset="0.25" />
    <GradientStop Color="Yellow" Offset="0.49" />
    <GradientStop Color="Orange" Offset="0.5" />
    <GradientStop Color="Orange" Offset="0.74" />
    <GradientStop Color="Red" Offset="0.75" />
    <GradientStop Color="Red" Offset="1.0" />
</LinearGradientBrush>

Originally, the bars' 0% position was at the top (bar.Height = 0) and 100% was at the bottom (bar.Height = 15). The bar size would change by simply adjusting its Height property. Everything worked fine, but I would prefer the bars' 0% position to be at the bottom instead, ie. the bars will 'grow' upward.

Making the bars grow upward was no problem, but the problem I do have is that the gradient now moves with the rectangle, so that the top of the rectangle is always green, no matter how small it is. I understand that this is because I am now using Canvas.SetTop to move the top of the rectangles (bars) as well as change their height. How can I force the gradient to an absolute position, regardless of the position of the rectangle?

MonitorWidget alt text (background opacity)

Sorry, I know the image is small, but you should just be able to make out that the middle bar starts from the top (green) and grows downward, ending in orange (50% -75% value). The left bar starts at the bottom (this is what I want), but the gradient in this bar moves with the height... this is the problem. Note that I'll reverse the gradient when I can fix this problem, so that red will represent the top 25%. In this example, the bottom quarter should be red, the next quarter orange and the remainder yellow.

I can't believe that there is no simple solution for this... come on brain-boxes. :) How about moving the absolute position of the gradient with the rectangle... is this possible???

(Relating post: http://stackoverflow.com/questions/3150956/determining-a-computers-maximum-hard-drive-data-transfer-rate-programmatically-w)

+1  A: 

I solved a similar issue using clipping. Create rectangles for 100% and fill with the brush. Then use clipping to only show part of the rectangle, see sample code below which shows two rectangles:

<Window x:Class="GradientTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">

    <Window.Resources>

        <LinearGradientBrush x:Key="Quarters" StartPoint="0,1" EndPoint="0,0">
            <GradientStop Color="LightGreen" Offset="0.0" />
            <GradientStop Color="Yellow" Offset="0.25" />
            <GradientStop Color="Orange" Offset="0.5" />
            <GradientStop Color="Red" Offset="0.75" />
        </LinearGradientBrush>

    </Window.Resources>

    <Canvas Height="100">

        <!--21%-->
        <Rectangle Fill="{StaticResource Quarters}" Width="30" Height="100" Canvas.Bottom="0" Canvas.Left="0">
            <Rectangle.Clip>
                <RectangleGeometry Rect="0, 79, 30, 100" />
            </Rectangle.Clip>
        </Rectangle>

        <!--68%-->
        <Rectangle Fill="{StaticResource Quarters}" Width="30" Height="100" Canvas.Bottom="0" Canvas.Left="40">
            <Rectangle.Clip>
                <RectangleGeometry Rect="0, 32, 30, 100" />
            </Rectangle.Clip>
        </Rectangle>

    </Canvas>

</Window>
Wallstreet Programmer
I tried this, but it doesn't seem to affect the gradient at all.
Sheridan
The sample do not affect the gradient, however it will only display part of it, for example the 21% rectangle in the sample is lightgreen with no red.
Wallstreet Programmer
Thanks, but I'm not sure I understand this completely. Are you suggesting that I create one rectangle for each of the four differently coloured 25% sections in the gradient? If so, how an where does the moving percentage level rectangle come in to it?
Sheridan
See above code. The rectangles should start at the bottom and got to the top of the canvas. Then you cut out using clipping the top part of the rectangle. For example you want to display a column corresponding to 68%. That means you will have to cut out 32% (100-68=32) of the rectangle. In the above code I do that for the second column. You probably need to use a value converter to perform the subtraction.
Wallstreet Programmer
Thank you for your explanation. It's a shame I can't give you both a tick, as @geoff used your clipping idea in the solution.
Sheridan
+1  A: 

Update

Clip the rectangle with values set at runtime.

    <Window.Resources>    
        <LinearGradientBrush x:Key="Quarters" StartPoint="0,0" EndPoint="0,1" >
            <GradientStop Color="LightGreen" Offset="0.0" />
            <GradientStop Color="LightGreen" Offset="0.24" />
            <GradientStop Color="Yellow" Offset="0.25" />
            <GradientStop Color="Yellow" Offset="0.49" />
            <GradientStop Color="Orange" Offset="0.5" />
            <GradientStop Color="Orange" Offset="0.74" />
            <GradientStop Color="Red" Offset="0.75" />
            <GradientStop Color="Red" Offset="1.0" />
        </LinearGradientBrush> 
    </Window.Resources>

    <Canvas Height="100">    
        <Rectangle Fill="{StaticResource Quarters}" Width="30" Height="100" Canvas.Bottom="0" Canvas.Left="0">
            <Rectangle.Clip>
                <RectangleGeometry x:Name="ClipRect" Rect="0, 100, 30, 100" />
            </Rectangle.Clip>
        </Rectangle>    
     </Canvas>

Code:

ClipRect.Rect = new Rect(0, 20, 30, 100); //80%
ClipRect.Rect = new Rect(0, 70, 30, 100); //30%

Original Solution

Use 2 rectangles for each bar.

One rectangle with 100% height and the colored gradient. Then overlay a black rectangle over the first rectangle. Adjust the black rectangles height(as you were before) to expose the gradient below.

eg If the value is 25% set the overlay height to 75%

geoff
Ordinarily, this would be just what I was looking for, but the user has control over opacity. If the overlayed black rectangle matched the opacity, the coloured gradient would show through and if not, they would have to have a different opacity to the background. I will remember this technique for future projects though, so thanks.
Sheridan
@Sheridan - Okay I see this issue with my solution. Updated my answer to use a clip rectangle instead(adapted from @Wallstreet Programmers answer)
geoff
That is exactly what I was after, thank you so much... I knew there must be a simple way to do it.The only difference was that the Height of the clipRect also has to be adjusted as well as the location:cpuClip.Rect = new Rect(0, 15 - barSize, 5, barSize);
Sheridan