Binding from ItemsSource context - c#

I'm having a problem with the DataContext and the Title. The following works as intended:
<chartingToolkit:LineSeries Title={Binding TrendDaily.Name} ItemsSource="{Binding TrendDaily.Progress}">
//...
</chartingToolkit:LineSeries>
But the Title should contain more information so I'm doing this:
<chartingToolkit:LineSeries ItemsSource="{Binding TrendDaily.Progress}">
<chartingToolkit:LineSeries.Title>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TrendDaily.Name}"/>
<TextBlock Text="-test text"/>
</StackPanel>
</chartingToolkit:LineSeries.Title>
//...
</chartingToolkit:LineSeries>
I figured out the Title binding doesn't work because it has the "Progress" elements as his context but I wasn't able to find a working binding.
Edit:
The complete new code with binding error (Cannot find source for binding with reference 'ElementName=LineName'):
<Window x:Class="WpfApplication1.MainWindow"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<chartingToolkit:Chart Title="Trend">
<chartingToolkit:Chart.Series>
<chartingToolkit:LineSeries DataContext="{Binding TrendDaily}"
ItemsSource="{Binding Progress}" DependentValuePath="Value" IndependentValuePath="Key" x:Name="LineName">
<chartingToolkit:LineSeries.Title>
<TextBlock>
<Run Text="{Binding DataContext.Name, ElementName=LineName}"/>
<Run Text="*"/>
</TextBlock>
</chartingToolkit:LineSeries.Title>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public TrendDailyClass TrendDaily { get; set; }
public MainWindow()
{
TrendDaily = new TrendDailyClass();
DataContext = this;
InitializeComponent();
}
}
public class TrendDailyClass
{
public Dictionary<string, double> Progress { get; set; }
public string Name { get; set; }
public TrendDailyClass()
{
Progress = new Dictionary<string, double>();
Progress.Add("10", 10);
Progress.Add("20", 20);
Name = "test";
}
}

Bind TrendDaily to the DataContext of LineSeries, then use DataContext in the inner bindings, using ElementName as:
<chartingToolkit:Chart Title="Trend"
DataContext="{Binding TrendDaily}"
x:Name="LineName">
<chartingToolkit:LineSeries ItemsSource="{Binding Progress}">
<chartingToolkit:LineSeries.Title>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DataContext.Name, ElementName=LineName}"/>
<TextBlock Text="-test text"/>
</StackPanel>
</chartingToolkit:LineSeries.Title>
//...
</chartingToolkit:LineSeries>
Moreover, there is no need to use two TextBlock.. You can use Run (which is very lightweight class) as:
<StackPanel Orientation="Horizontal">
<TextBlock>
<Run Text="{Binding DataContext.Name, ElementName=LineName}"/>
<Run Text="-test text"/>
</TextBlock>
</StackPanel>
It's better, as it avoids unnecessary visual element. Classes derived from UIElement are relatively heavier.

If you're first code example is working, you should be able to use the StringFormat property in your first binding:
<chartingToolkit:LineSeries Title={Binding TrendDaily.Name, StringFormat='{}{0}-test text'} ItemsSource="{Binding TrendDaily.Progress}">
//...
</chartingToolkit:LineSeries>

Related

List of objects and ComboBoxes

Here's my problem:
I've got these classes:
public class CsvField
{
public string Content { get; set; }
public CsvField(string content)
{
Content = content;
}
}
public class CsvLine
{
public List<CsvField> Fields = new List<CsvField>();
public int LineNumber;
}
public static class Settings
{
public static List<string> Tags = new List<string>();
public static CsvLine AllHeaders = new CsvLine();
}
What I want to do, is display the ListBox containing every member of Settings.AllHeaders.Fields and a ComboBox containing all members of Settings.Tags list (placed horizontally - a member of AllHeaders on the left and a ComboBox next to it). So if I had 4 headers, I would get a List of those 4 headers and 4 ComboBoxes, each of them next to individual header. Each of these ComboBoxes would contain a list of tags.
So, I defined a DataTemplate:
<Window x:Class="CSV_To_Tags_App.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:CSV_To_Tags_App"
Title="Window2" Height="435" Width="566">
<Window.Resources>
<DataTemplate DataType="{x:Type loc:CsvField}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="HeaderTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Text="{Binding Content}"
/>
<ComboBox HorizontalAlignment="Right" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Label Content="Available headers" HorizontalAlignment="Left"
VerticalAlignment="Top"/>
<ListBox x:Name="HeadersListtListBox" HorizontalAlignment="Left"
Height="254" Margin="36,104,0,0" VerticalAlignment="Top" Width="452"
ItemsSource="{Binding}"/>
</Grid>
</Window>
Now, XAML code above is incomplete, because I don't know how to:
1. Bind TextBlock to Settings.AllHeaders.Fields.Content
2. Bind ComboBox to Tags List
The link provided by #MD's does provide the solution to what you are asking in your question, but you have to rearrange things a little bit to see it.
The XAML below will give you what you are asking for, assuming your class that you are binding to is set up properly for data binding (implementing the INotifyPropertyChanged interface). There are lots of examples on this site of how to properly implement that portion.
<Window x:Class="CSV_To_Tags_App.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:CSV_To_Tags_App"
Title="Window2" Height="435" Width="566">
<Grid>
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Path=Settings.AllHeadings.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Content}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=Settings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=Tags}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Window>

Binding to TextBlock in ListView

I don't know if my understanding of binding is just poor or if I am not seeing the problem, but I hope someone can help me out here. I have a ListView with a template of an image and a TextBlock, I need the TextBlock to be bound to the ItemsSource of the ListView. However when I run this I get nothing shown, I don't even see my image that I have set.
XAML:
<UserControl.Resources>
<FontFamily x:Key="FontFamily">MS Reference Sans Serif</FontFamily>
</UserControl.Resources>
<Grid>
<ListView BorderThickness="0" ItemsSource="{Binding Facies}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/Shale.png"/>
<TextBlock Text="{Binding FaciesName}" Width="75" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
C#:
public partial class FaciesControl : UserControl
{
public FaciesControl()
{
InitializeComponent();
}
public List<string> Facies {get; set;}
public void Bind(string[] data)
{
Facies = new List<string>();
Facies.AddRange(data);
}
}
First set DataContext like this:
public FaciesControl()
{
InitializeComponent();
string[] str = { "Name1", "Name2", "Name3" };
Bind(str); // Make sure you have called the Bind method
DataContext = Facies;
}
Second change your XAML like this:
<ListView BorderThickness="0" ItemsSource="{Binding}">
....
....
<TextBlock Text="{Binding}" Width="75" Margin="5"/>

Reuse XAML in WPF

I have some XAML I want to reuse. I can easily create a custom control and use it, but I'd rather not. Here's what I've tried:
<Window.Resources>
<Expander x:Key="Namespacer" x:Shared="False" Name="NS" Background="SkyBlue">
<StackPanel Name="ClientArea" Margin="20,0,20,0">
<StackPanel Name="Usings" Grid.Row="0" Height="Auto"></StackPanel>
<StackPanel Name="Structs" Grid.Row="1" Height="Auto"></StackPanel>
<StackPanel Name="Classes" Grid.Row="2" Height="Auto"></StackPanel>
<StackPanel Name="IFaces" Grid.Row="3" Height="Auto"></StackPanel>
<StackPanel Name="Delegates" Grid.Row="4" Height="Auto"></StackPanel>
<StackPanel Name="Enums" Grid.Row="5" Height="Auto"></StackPanel>
<StackPanel Name="Nested" Grid.Row="6" Height="Auto"></StackPanel>
</StackPanel>
</Expander>
</Window.Resources>
<StackPanel>
<ContentControl Name="N1" Content="{StaticResource Namespacer}" />
</StackPanel>
Now, I want to do something like:
this.N1.Header = "SomeTitle.Namespace1";
And also be able to add new chunks of XAML to my stack panels in N1 in a similar fashion. How to achieve that?
Well, you could do this:
((Expander)(this.N1.Content)).Header = "SomeTitle.Namespace1";
But that gets ugly. I'd recommend switching to data binding. Here's an example.
First, here's a data class with the structure I think you're going for:
public partial class MainWindow : Window
{
public class MyData
{
public string ItemTitle { get; set; }
public IList<string> Usings { get; set; }
public IList<string> Structs { get; set; }
}
public class MyViewModel
{
public IList<MyData> MyBoundData { get; set; }
}
public MainWindow()
{
var d1 = new MyData{
ItemTitle = "thing1",
Usings = new[]{"a", "b"}
};
var d2 = new MyData{
ItemTitle = "thing2",
Structs = new[]{"c","d"}
};
this.DataContext = new MyViewModel{
MyBoundData = new[]{ d1, d2}
};
InitializeComponent();
}
}
And here's an items control bound to our data:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl ItemsSource="{Binding MyBoundData}" Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding ItemTitle}" Background="SkyBlue">
<StackPanel>
<Expander Header="Usings" Background="SkyBlue">
<ItemsControl ItemsSource="{Binding Usings}"/>
</Expander>
<Expander Header="Structs" Background="SkyBlue">
<ItemsControl ItemsSource="{Binding Structs}"/>
</Expander>
</StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Note that the items control has a DataTemplate that corresponds to your "Namespacer" xaml chunk. You could, of course, move the DataTemplate chunk into the window resources like you have in your example, if you want to use it in more than one ItemsControl.

How to use more than one DataContext

Im using MVVM Light, and in my Locator I have two ViewModels. However in a page I want to use more than one ViewModels to use their properties in the page's ui elements, but how?
Here is the XAML of my Page:
<Page
x:Class="my_app.MainMenuPage"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:my_app"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Foreground="Red"
DataContext = "{Binding Source={StaticResource Locator}, Path=SettingsVM }">
Here is the code of my Locator:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<StudentsViewModel>();
SimpleIoc.Default.Register<SettingsViewModel>();
}
public StudentsViewModel StudentsVM
{
get
{
return ServiceLocator.Current.GetInstance<StudentsViewModel>();
}
}
public SettingsViewModel SettingsVM
{
get
{
return ServiceLocator.Current.GetInstance<SettingsViewModel>();
}
}
public static void Cleanup() {}
}
So i can't do something like this, obviously:
DataContext = "{Binding Source={StaticResource Locator}, Path=SettingsVM, Path=StudentsVM}">
As far as I can see you don't use 2 DataContext. You use 2 objects of one DataContext. Set DataContext to Locator (without any Path) and then specify Path=StudentsVM.PropertyA or Path=SettingsVM.PropertyC per binding
<Page ... DataContext="{Binding Source={StaticResource Locator}}">
<!-- .... -->
<TextBlock Text="{Binding StudentsVM.PropertyA}"/>
<TextBlock Text="{Binding StudentsVM.PropertyB}"/>
<TextBlock Text="{Binding SettingsVM.PropertyC}"/>
<TextBlock Text="{Binding SettingsVM.PropertyD}"/>
<!-- .... -->
</Page>
or if you have more properties to bind you can locally change DataContext for group of controls
<Page ... DataContext="{Binding Source={StaticResource Locator}}">
<!-- .... -->
<StackPanel DataContext="{Binding StudentsVM}">
<TextBlock Text="{Binding PropertyA}"/>
<TextBlock Text="{Binding PropertyB}"/>
</StackPanel>
<!-- .... -->
<StackPanel DataContext="{Binding SettingsVM}">
<TextBlock Text="{Binding PropertyC}"/>
<TextBlock Text="{Binding PropertyD}"/>
</StackPanel>
<!-- .... -->
</Page>

DataTemplate and ContentControl when user class as DataContext

What I have:
User class
public class MyButton
{
public String ButtonProperty { get; set; }
public String LabelProperty { get; set; }
public MyButton()
{
ButtonProperty = "MyButtonText!";
LabelProperty = "LabelText!";
}
}
DataTemplate defined in window resources
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyButton}">
<Border Width="100" Height="100" BorderThickness="2" BorderBrush="Aquamarine">
<StackPanel >
<Button>
<TextBlock Text="{Binding ButtonProperty}"></TextBlock>
</Button>
<Label Content="{Binding LabelProperty}"></Label>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
I want to DataTemplate will draw instead of instance of MyButton class
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication7"
Title="MainWindow" Height="500" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyButton}">
<Border Width="100" Height="100" BorderThickness="2" BorderBrush="Aquamarine">
<StackPanel >
<Button>
<TextBlock Text="{Binding ButtonProperty}">
</TextBlock>
</Button>
<Label Content="{Binding LabelProperty}">
</Label>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<!-- Create instance of MyButton in XAML-->
<local:MyButton></local:MyButton>
</Window>
It works fine, but it is not what I want at the end. What if instance of MyButton will DataContext for Window?
public MainWindow()
{
//Set instance of MyButton as DataContext
DataContext = new MyButton();
InitializeComponent();
}
I thought I must write that in XAML-side
<ContentControl DataContext="{Binding}">
<!--MyButton XAML code from DataTemplate here -->
</ContentControl>
instead of
<local:MyButton></local:MyButton>
but it doesn't work at all. what I am doing wrong?
You should try to bind to the Content property of your ContentControl instead of the DataContext property :
<ContentControl Content={Binding } />
Besides, the DataContext of the ContentControl is already the MyButton.
I'm not really sure what are you trying to achieve there. IF you simply want to extend the functionality of the default Button you could define attached properties
Why would you want the DataContext of your Window to be the Button? Maybe the other way around? Not sure I understood that part correctly.

Categories