I'm trying to create something looking like this :
It's designed to be an XAML title for VMIX software, video broadcasting purposes.
I'm gonna get a lot of datas from a GSheet, handle in VMIX, and assign those datas to my TextBlocks such as "Candidate", "City" and the Votes %.
From that % I want the bar size to increase/decrease, I managed to do part of that.
But the main issue is to get the % TextBlock margin to fit on the right of the rectangle. Anyone knows how I could do that ?
I have never been coding in C#, I have a background in C, C++ and JS, so I've spent my day looking for that purpose and couldn't make it right.
I saw some binding methods that could fit, but I'm unable to use them.
Moreover I'm working on Blend for Visual Studio 2017, and I don't get why I can't run some simple code on it when pressing F5... It's another problem thought.
Thanks a lot for your help.
EDIT :
I've reached something new so far, really DIY solution but it's my lsat solution if I can't find better :
I'll have 2 TextBlock for 1 ProgressBar (Thanks to Chris)
<Grid Margin="0,0,-8,0">
<TextBlock x:Name="Votes1" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="{Binding Text, ElementName=MarginVotes1}" FontSize="72" Width="853" Height="188"><Run Text="6"/><Run Text="00"/></TextBlock>
<ProgressBar HorizontalAlignment="Left" Height="79" Margin="171,503,0,0" VerticalAlignment="Top" Width="{Binding Path=Text, ElementName=Votes1}" Background="#FFEA4545"/>
<TextBlock x:Name="MarginVotes1" HorizontalAlignment="Left" Margin="171,587,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="72" Height="98" Width="550"><Run Text="8"/><Run Text="0"/><Run Text="0"/><Run Text=","/><Run Text="4"/><Run Text="9"/><Run Text="0"/><Run Text=",0,0"/>
</TextBlock>
So this works fine, but I have to prepare before what my "MarginVotes1" value is (in GoogleSheet).
The best would be directly in code behind to do something like this :
CONVERT Votes1.Text to Int STORE in val
SET x to val + DefaultMargin
CONVERT x to String STORE in MarginX
CREATE String MarginVoteStr as MarginX + ",500, 0, 0"
SET Votes1.Margin as MarginVoteStr
Welcome to WPF. Here's some code I put together that you should be pretty close to what you need.
XAML:
<ItemsControl Grid.IsSharedSizeScope="True" ItemsSource="{Binding Candidates}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Candidate" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"/>
<Rectangle Grid.Column="1" Height="10" Margin="5, 0" Width="{Binding BarWidth}" Fill="{Binding BarColor}"/>
<TextBlock Grid.Column="2" Text="{Binding Percentage, StringFormat=P}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Candidates = new List<Candidate> { new Candidate { Name = "Joe", Percentage = .50, BarColor = Brushes.Green},
new Candidate { Name = "Bob", Percentage = .30, BarColor = Brushes.Yellow},
new Candidate { Name = "Sarah", Percentage = .20, BarColor = Brushes.Gray}};
}
public List<Candidate> Candidates
{
get { return (List<Candidate>)GetValue(CandidatesProperty); }
set { SetValue(CandidatesProperty, value); }
}
public static readonly DependencyProperty CandidatesProperty =
DependencyProperty.Register("Candidates", typeof(List<Candidate>), typeof(MainWindow));
}
public class Candidate
{
public string Name { get; set; }
public double Percentage { get; set; }
public Brush BarColor { get; set; }
//This is just shorter syntax for a readonly property.
//The multiplier (200) should be whatever length you want a full bar to be
public double BarWidth => Percentage * 200;
}
There are a number of points you should note:
Whenever you need to display multiple data items in WPF, especially if the number of items is variable, you should be using some type of ItemsControl
.
An ItemsControl
takes a collection of some kind and displays each item using a DataTemplate
. An ItemsControl
creates a new instance of its ItemTemplate
for every item in its source collection. The link between the data and the visuals is established through data bindings .
Everything between the <DataTemplate>
tags is the visual layout of a single item.
Notice that I am not using Margin
to create the desired layout. Instead of using Margin
in that way, I'm using one of WPFs many Panel
controls: Grid
. With Grid
you can define rows and columns like a table.
Each item in my example is a Grid
with 1 row and 3 columns. The elements that make up the item are placed in that grid using the Grid.Column
property. Each column has Width="Auto"
, which means it will grow to accommodate the width of what's inside. IsSharedSizeScope
and SharedSizeGroup
make it so that the Grid
s of each individual item all have the same width for the first column.
Candidate
class This is the class that will be used to store and represent the data being displayed. Note that the property names match the {Binding ______}
values from the DataTemplate
.
My example main window has a collection of Candidate
objects stored in a dependency property . This property is bound to the ItemsSource
of the ItemsControl
.
The idea is to populate your collection with whatever data items you need and let the ItemsControl
take care of the rest, thus keeping the data and visuals of the project relatively independent. Even small visual aspects like formatting the percentage value correctly for display can be done using the DataTemplate
instead of writing the code in C#, as shown using StringFormat=P
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.