I'm using the XCeed Extended Toolkit Plus. The charting specifically. However, I don't think question need to be specifically about this control, more about how to pass or share values between controls and templates.
The charts are in a WPF UserControl, but the values are set in the code behind. There is no data binding or MVVM, more of a 'winforms' approach.
The charting works, but I want to alter the way the bar's (in a bar graph) display.
According to the documentation, I can use a Template
<xctk:Chart >
<xctk:Chart.Areas>
<xctk:Area x:Name="MyGraphArea">
<xctk:Area.XAxis>
<xctk:Axis Title="Date"
/>
</xctk:Area.XAxis>
<xctk:Area.YAxis>
<xctk:Axis Title="Position"
/>
</xctk:Area.YAxis>
<xctk:Area.Series>
<xctk:Series Template="{StaticResource SeriesTemplate}" >
<!--done in code behind-->
</xctk:Series>
</xctk:Area.Series>
</xctk:Area>
</xctk:Chart.Areas>
</xctk:Chart>
On the same XAML page, I also have in my Grid.Resources. This is where the problem is
<DataTemplate x:Key="SeriesTemplate">
<Button x:Name="Bar">
<StackPanel>
<DockPanel>
<TextBlock x:Name="seriesTemplateDate" Text="How To I Bind"></TextBlock>
</DockPanel>
<DockPanel>
<TextBlock x:Name="seriesTemplatePosition" Text="What Am I binding too"></TextBlock>
</DockPanel>
</StackPanel>
</Button>
</DataTemplate>
And in my code behind (showing constructor)
public GraphView(IEnumerable<DataPoint> graphData, string title)
{
InitializeComponent();
var series = new Series();
foreach (var dataPoint in graphData)
{
series.DataPoints.Add(dataPoint);
}
series.Title = title;
this.MyGraphArea.Series.Add(series);
}
So, the DataTemplate is where I'm having an issue. I have no idea what to enter the for text value
I don't think I can add the value by name and set it in the code behind because it gets called on each iteration (depending on how many items are in the series).
The only way, in my head, is if it the DataTemplate can inherit some how from the calling control. At this stage, my Google results were providing nothing useful, and I think I'm getting myself muddled!
I have to guess since I do not know the XCeed Extended Toolkit Plus. That being said, the Datacontext of a Datatemplate is usually set to the data object. In this case that should be the series object. This means you can bind directly to properties on the series object like the Title-property. Maybe it also has a Date and Position property?!
series.Title = title;
series.Date = date;
series.Position = position;
<TextBlock x:Name="seriesTemplateTitle" Text="{Binding Title}"></TextBlock>
<TextBlock x:Name="seriesTemplateDate" Text="{Binding Date}"></TextBlock>
<TextBlock x:Name="seriesTemplatePosition" Text="{Binding Position}"></TextBlock>
Good Luck!
Related
I'm new to Windows 8.1 development, XAML, and C#, so if this question is rudimentary, please forgive me.
I've got a <Page> in my app that contains a <ListView>, like so:
<ListView ItemsSource="{Binding Mode=TwoWay}" x:Name="ListView_Statistical">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Width="100" Margin="10,20">
<Run Text="X/Y " />
<!--<Run Text="{Binding Source={StaticResource ThisPage}, Path=i}" />-->
</TextBlock>
<TextBox HorizontalAlignment="Left" Text="{Binding xVal}" PlaceholderText="X" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
<TextBox HorizontalAlignment="Left" Text="{Binding yVal}" PlaceholderText="Y" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the Code Behind, I set up its DataContext like so:
ListView_Statistical.DataContext = this.statisticalPoints;
this.statisticalPoints is defined as such:
public ObservableCollection<StatisticalPoint> statisticalPoints
{
get { return (ObservableCollection<StatisticalPoint>)GetValue(statisticalPointsProperty); }
set {
SetValue(statisticalPointsProperty, value);
NotifyPropertyChanged("statisticalPoints");
}
}
// Using a DependencyProperty as the backing store for statisticalPoints. This enables animation, styling, binding, etc...
public static readonly DependencyProperty statisticalPointsProperty =
DependencyProperty.Register("statisticalPoints", typeof(ObservableCollection<StatisticalPoint>), typeof(EnterCalc), new PropertyMetadata(0));
I'm not sure if making this a DependencyProperty is necessary, or if making it follow INotifyPropertyChanged is necessary, but they don't seem to hurt.
Anyway, so in my constructor, I add a bunch of stuff to my statisticalPoints:
this.statisticalPoints = new ObservableCollection<StatisticalPoint>();
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 1.0, yVal = 2.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 33.0, yVal = 44.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 555.0, yVal = 666.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 0.7, yVal = 0.8 });
And when I load up the page, I do indeed see five rows in my ListView, populated as defined in my initialization of this.statisticalPoints.
The part I'm having trouble with is this:
I change the first value in the first <TextBox> in the ListView, then hit my save button... but ListView.Items doesn't have my change reflected, and I can't figure out how to look at the <TextBox> itself.
What I really want to do is have this bunch of statistical points modifiable by my user and be able to save their changes. To do that, I feel like I need to read the value in the <TextBox>es, but I can't figure out how to do that.
Alternatively, if the 'right way' to do this is to keep the data in this.statisticalPoints up-to-date when changes are made in the <TextBox>es, then I thought that a TwoWay binding Mode would do it, but neither ListView.Items nor this.statisticalPoints is changed when I make changes in the <TextBox>.
I do not have event handlers set up in those <TextBox> elements, as you can see, but do I need them, or am I missing something obvious?
Thanks in advance for any help you can give me!
To solve your initial problem, make the binding for each text box Mode="TwoWay". For reasons beyond my comprehension, the mode is default OneWay on pretty much everything in Windows Store Apps.
Making the ItemsSource binding two way does next to nothing, as the UI isn't changing the collection itself (by changing, I mean completely replacing). To iterate over your collection, just iterate over this.statisticalPoints and it will have the current data.
Now, you have a ton of other misconceptions so to try and run through them:
You never showed your save button, but the bindings either update your source or they don't. A save button is usually used to persist changes from the view model to the model.
Speaking of view models, you don't appear to have one. You shouldn't be directly setting the data context of controls, and certainly shouldn't have so much in code-behind. Create a proper view model object for your page, and bind ItemsSource to a public property of that view model.
NotifyPropertyChanged on a collection usually is unnecessary unless you are replacing the collection in code.
Having it won't hurt though, except, the setter of the backing property of a DependencyProperty (DP) is never called by the framework, so putting it there is just weird
And you don't really need a DP at all. DPs are there so a parent control can bind data to your special user control. Until you are using user controls, and really understand how DPs work, you shouldn't need to use them.
I am attempting to make a WPF application. The application needs to use a "list view" to show results of queries to the database. I have been able to successfully create the application (GUI, database, LINQ, etc.), however, the display of my query results appear more "gridlike".
The specifications for the project below show that each record that appears in the results needs to have a green circle icon next to it. I have removed the actual results from the images below to keep the contents of the database private.
I don't have enough Reputation Points to post images, so I posted pictures so a sample/testing domain that I use. You can see screenshots here of the WPF app and code here:
http://digitalworkzone.com/WPF.html
What am I doing incorrectly? Is there something I need to add or modify to my code to be able to get the green circles and more of a "list" style to display my query results?
Understand the WPF content model. http://msdn.microsoft.com/en-us/library/bb613548.aspx
Anything that has a 'Content' property basically behaves in two ways. If the 'Content' is set to something that derives from UIElement, then the class will manage it's own presentation. Anything else, however, will just get .ToString() called, and it's text displayed instead.
What this means in the long run is that everything in WPF can display anything. If you want to show a button in a button, you can. For example:
<Button>
<Button.Content>
<Button Content="This will show as text" />
</Button.Content>
</Button>
The inner button will have text, but the outer button will show a Button because Button derives from UIElement and therefore will handle its own presentation.
In your picture examples above, you have ListBoxes/DataGrids that you want to fill in with graphical information. Try this out:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<Button Content="One"/>
<Button Content="Two"/>
<Button Content="Three"/>
<Button Content="Four"/>
</ListBox.Items>
</ListBox>
Now you have a ListBox that shows Buttons instead of Text. You can take this a step further and contain the items in a stackpanel, for example:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
</ListBox.Items>
</ListBox>
Now we have items that contain a layout container (StackPanels, which then contains other elements).
However, if you set the ItemsSource elsewhere, you can actually use a DataTemplate to display the contents. A DataTemplate in effect targets a particular class and lays out it's contents as defined in XAML. Consider:
Code Behind:
public partial class MyWindow : UserControl {
public MyWindow() {
InitializeComponent();
MyListBox.ItemsSource = new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White"),
};
}
XAML:
<ListBox HorizontalContentAlignment="Stretch" x:Name="MyListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when the Listbox displays, it will cycle through each of the items in the ItemsSource property, and then lay them out using the DataTemplate. It's possible to have the DataTemplate target specific classes by using the DataType property if you're using polymorphism (as in different types of people such as 'Cusomters' or 'Employees' which all derive from 'Person).
The problem with this approach is that you are setting the value of the items directly, which is bad form. It's better to define a class that handles all of the data for your view separately. Consider:
public class ViewModel {
// WPF will automatically read these properties using reflection.
public List<Person> People {
get {
return new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White")
};
}
}
}
That will hold all the data for the view, now let's add it to the actual window. First we need to reference the namespace ('xmlns' means xml namespace):
<Window x:Class="Sharp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lol="clr-namespace:Sharp">
The namespace is Sharp (the namespace where my stuff lives), and the alias we'll give it is lol. Now we attach our ViewModel class to the window by setting it to the DataContext property, as in:
<Window>
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
</Window>
This makes all of the public properties on the ViewModel class available to the Window. This way, if we want to read the Persons information into our ListBox, we simply say:
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding People}" >
...
</ListBox>
Notice that we say ItemsSource={Binding People}, which means 'scan the ViewModel for any public properties called 'People' and then retrieve those results. This is essentially the fundamentals behind the MVVM approach. You might have all of your business logic in one or many classes which handle the main application operation in a Model, but then you have a ViewModel which interacts with the Model and exposes the results as public properties. WPF automatically binds to those properties and presents them for your. The information just flows, rather than setting the values by force.
To really understand how WPF is supposed to work, you should take some time to understand the basics of MVVM. WPF was really designed with MVVM in mind, and so to really get how WPF is supposed to work, you really should take the time to get your head around it. Take a look at:
http://agilewarrior.wordpress.com/2011/01/11/simple-mvvm-walkthrough-part-i/ .
<ListBox ItemsSource="{Binding QueryResults}">
<ListBox.ItemsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}"/>
<TextBlock Text="{Binding TextSource}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemsTemplate>
</ListBox>
Will work if you have a list of objects named QueryResults in your code behind. Each object needs to have an string property named ImageSource and a string property named TextSource.
However, since you only need to display a green circle icon for each of the items, you can hardcode the image source. The above will work if you want to have a different icon for each, though.
Also note that in order for this to work, you need to set the DataContext of the window to DataContext="{Binding RelativeSource={RelativeSource Self}}"
I'm quite new to C# and Windows Phone 7 for that sake, but none the less, I've thrown myself into trying to make a small app for myself. Here's my problem:
I'm trying to set up a DataTemplate that will position my Name and Drinks variables that I've declared in MainPage.xaml.cs. Here's my action when button1 is clicked:
private void button1_Click(object sender, RoutedEventArgs e)
{
string Name = participantName.Text;
int Drinks = 0;
listBox1.Items.Add(Name + Drinks);
}
And here is my DataTemplate from MainPage.xaml
<ListBox Height="Auto" HorizontalAlignment="Stretch" Margin="7,74,0,0" Name="listBox1" VerticalAlignment="Stretch" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<TextBlock Text="{Binding Path=Name}" FontSize="35" />
<StackPanel Width="370">
<TextBlock Text="{Binding Path=Drinks}" FontSize="35" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is that my data is not shown. It works perfectly without the DataTemplate, but as soon as I use it, my text simply doesn't get through. Your help is very much appreciated.
The template itself is ok. The bindings on the template, though, are currently incorrect.
When you add a new item to the list box, you are just adding a plain old string (which is currently missing a space, BTW.) Your bindings, though, expect the object in the list to have a Name property and a Drinks property, which of course the string class does not have.
The usual solution here is to logically separate your data model from your presentation, by creating a class to store the data itself (probably PersonDrink, with the appropriate Name and Drinks properties) and then adding those objects to the list.
You should read up on the MVVM pattern, as it provides an excellent way to ensure that changes in your data are reflected in your view, and visa versa.
http://amarchandra.wordpress.com/2011/12/18/binding-multiple-object-in-wp7-using-listbox/
Here is a sample for binding data using a datatemplate. I hope this might help you.
I'm trying to create a simple header template for an accordion object in silverlight 4.
I've added an image and a TextBlock to the header template of the AccordionItem. I want to hide or show the image dependant on the values entered on the page.
Because i want to bind these values directly to the actual accordion item, I've created a new type 'AccordionItemWithIcons' that simply inherits from AccordionItem but adds a couple of dependancy properties to handle this. I'm only showing a couple of those properties for brevity. :)
So, here's my accordion with my 'AccordionItemWithIcons' control. Note that the property 'CheckIsVisible' is of type 'Visibility'
<Grid x:Name="LayoutRoot">
<Controls:Accordion Height="100">
<my:AccordionItemWithIcons
x:Name="FirstItem"
Content="Content Text"
Header="Header Text"
CheckIsVisible="Collapsed"
EventSummary="Summary Text"
HeaderTemplate="{StaticResource AccordionItemHeaderTemplate1}"/>
</Controls:Accordion>
</Grid>
And here is the header template.
<DataTemplate x:Key="AccordionWithIcons_HeaderTemplate1" >
<Grid >
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<TextBlock Text="{Binding EventSummary}" />
<Image Visibility="{Binding CheckIsVisible}" Source="/Labyrinth;component/cross.png"/>
</StackPanel>
</Grid>
</DataTemplate>
Can anyone explain how I can bind the TextBlock's text and the Image's Visibility to the values set in the underlying AccordionItemWithIcons object? I've spent hours messing about with different DataContext's and sources and cannot seem to get this to work!
I don't know if helps to explain what I'm trying to achieve, but ultimately in the code behind i want to be able to say something like (shown below), to show or hide the icon in the header template.
FirstItem.CheckIsVisible = Visibility.Visible
For this, there exists a VisibilityToBooleanConverter
<BooleanToVisibilityConverter x:Key=”boolVisConverter”/>
[...]
Visibility="{Binding ElementName=anyCheckbox,
Path=IsChecked,
Converter={StaticResource boolVisConverter}}"
Assuming I have a simple class that represents a staff member
class Staff
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int SecondsAlive { get; set; }
}
and I have a DataTemplate for staff
<DataTemplate DataType={x:Type Staff}>
<StackPanel Orientation="Horizontal">
<TextBlock Text={Binding FirstName}/>
<TextBlock Text=" ">
<TextBlock Text={Binding FamilyName}/>
<StackPanel.ToolTip>
<TextBlock Text={Binding SecondsAlive}/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
I then show a whole bunch of staff in a ListBox
myListBox.ItemsSource = GetAllStaff();
Pretty standard stuff. The problem I have is that the tooltip which shows the number of seconds that someone has been alive does not get updated. When you first mouse over a staff member then it works fine but from then on it keeps that value for ever. I could implement INotifyPropertyChanged to get around this but it seems like overkill to do this for every staff member whenever SecondsAlive changes. Say I have 400 staff in the list then I have to raise 400 events even though the user might never look at another tooltip. What I would like is to make the tooltip request the SecondsAlive property ever time it is shown. Is that possible?
Please note that this is just an example and I don't need to know how many seconds my staff have been alive :-) But I have the same issue that I need to raise an even around 400 times just for a tooltip which someone probably won't look at.
OMG!!! I have finally found the solution to this problem!!! This has been bugging me for months. I'm not surprised no one answered this because the code I typed out at the top actually DIDN'T show the problem I was trying to reproduce, in fact it showed the solution. The answer is that if you define your tooltip like this
<StackPanel.ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</StackPanel.ToolTip>
Then everything works just fine and dandy and there is no need to raise a propertyChanged event on "SecondsAlive". The framework will call the SecondsAlive property every time the tooltip is shown. The problem comes when you define your tooltip like this:
<StackPanel.ToolTip>
<ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
Having the extra tooltip tag in there makes sense, surely you need to create a tooltip object to assign it to the tooltip property but this is incorrect. What you are assigning to the tooltip property is actually the content of the tooltip. I was assuming you needed to give it controls such as textblock and image to display but you can pass in anything and it will display the content just like a content control. Seeing it inherits from content control this makes sense :-) It all seems obvious once you know :-)
Thanks everyone for looking at this.
PS. I found an additional problem in that the next logical step in simplifying code is to just assign text straight to the tooltip like this (assuming your tooltip is plain text):
<TextBlock Text="{Binding Path=StaffName}" ToolTip="{Binding Path=StaffToolTip}"/>
This also causes the original problem I was having. This makes sense because the results of the property StaffToolTip get assigned to the tooltip property and never get called again. However, it doesn't quite make sense why then assigning a TextBlock to the tooltip property actually solves the problem.
Although this is an old question.
In this case:
<StackPanel.ToolTip>
<ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
The ToolTip control is hosted by an isolate HWND (a.k.a, a native window). It should have its own DataContext, the correct binding expresion should be like:
<StackPanel.ToolTip>
<ToolTip
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
In this particular case there is a cool trick you can use
Seconds Alive Now = Seconds Alive originally + Elapsed Time
You can bind to the Elapsed Time property and specify a converter that adds the initial value to it. That way you only need to raise 1 event and the tooltips would all be updated.
Edit: You can add the ElapsedTime property (with INotifyPropertyChanged) to many places -- one logical place could be to the collection that is storing your Staff objects
Edit: You would also need to bind each tooltip to the shared ElapsedTime property rather than the SecondsAlive property
It's worth noting that the ToolTip appears to check your object that it's bound to for equality before reloading itself with the new data.
In my case I did an override of
public override bool Equals(object obj)
and
public override int GetHashCode()
on a class with properties
public class MultipleNameObject { string Name, string[] OtherNames};
Unfortunatley I only did a string.Compare() on the Name property of the MultipleNameObject for equality purposes. The tool tip was supposed to display OtherNames in a ItemsControl, but was not updating if Name was equal on the previous MultipleNameObject that the mouse hovered been over on the grid, even if the OtherNames were different.
[edit] Running with debug enabled confirms that the GetHashCode() override of my object was being used by the ToolTip to decide whether to grab the new data back. Fixing that to take the string[] OtherNames into account fixed the problem.
This example shows how to add a tooltip to a grid that recalculates the tooltip on demand, when the user hovers over a cell in the grid.
This is useful if you have a huge grid with 10,000 items, and you want to update the grid continuously - but you don't want to update the tooltips continuously, as this is time consuming.
This example is for Infragistics, but the principle applies equally to other fine libraries such as DevExpress.
Xaml
<ig:TemplateColumn Key="ColumnKey">
<ig:TemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="Header"></TextBlock>
</DataTemplate>
</ig:TemplateColumn.HeaderTemplate>
<ig:TemplateColumn.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel.ToolTip>
<TextBlock Text="{Binding ToolTip}"/>
</StackPanel.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter" >
<i:InvokeCommandAction Command="{Binding ToolTipHoverRefreshCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</ig:TemplateColumn.ItemTemplate>
</ig:TemplateColumn>
ViewModel
public ICommand ToolTipHoverRefreshCommand => new DelegateCommand(() =>
{
this.OnPropertyChanged(this.ToolTip);
});
public string ToolTip
{
get
{
return DateTime.Now.ToString();
}
set
{
this.OnPropertyChanged(nameof(this.ToolTip));
}
}
I was having the same problem of it not updating. I found the solution to be adding the controls to a ToolTip template:
<DataTemplate DataType={x:Type Staff}>
<StackPanel Orientation="Horizontal">
<TextBlock Text={Binding FirstName}/>
<TextBlock Text=" ">
<TextBlock Text={Binding FamilyName}/>
<StackPanel.ToolTip>
<Tooltip>
<ToolTip.Template>
<ControlTemlate>
<TextBlock Text={Binding SecondsAlive}/>
</ControlTemplate>
</ToolTip.Template>
</Tooltip>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
I don't quite understand why this is needed or why this makes it behave differently but it fixed the problem.