Binding into another usercontrol - c#

I just started working with WPF and I'm trying to wrap my head around the XAML Bindings.
After a lot of reading I thought I had an easy enough usecase to test my knowledge. But nada :-( I can't figure it out.
What I'm trying to do is the following:
I have Address-Objects:
public IEnumerable<Address> Addresses
{
get
{
if (addresses == null)
{
addresses = new Repository<Address>().GetAll().ToList<Address>();
}
return addresses;
}
}
which have Rental-Objects e.g.
address.Rentals
also as IList.
On my MainWidow I want to have 2 UserControls, one for the address and one for the rental information. If I click the Navigator on the address user control, I only want to see the rental records of the current address.
MainWindow
<Window x:Class="Swopt.Mira.WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:local="clr-namespace:Swopt.Mira.WPFApp"
Title="MainWindow" Height="500" Width="525">
<Window.Resources>
<sampleData:DataContext x:Key="testDataContext" />
</Window.Resources>
<Grid x:Name="rootGrid" Background="White" DataContext="{StaticResource testDataContext}">
<StackPanel>
<local:RentalCustomer x:Name="rentalCustomer" Height="100" />
<local:Rentals Height="100" />
</StackPanel>
</Grid>
Address
<UserControl
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:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="Swopt.Mira.WPFApp.RentalCustomer"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="RentalCustomerUserControl">
<Grid x:Name="rootGrid" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="Firma1"/>
<TextBox x:Name="Firma1" Text="{Binding ElementName=collectionNavigator, Path=CurrentItem.Firma1}" />
</StackPanel>
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
Grid.ColumnSpan="2"
x:Name="collectionNavigator"
Source="{Binding Addresses}"
/>
</Grid>
Rentals
<UserControl
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:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="Swopt.Mira.WPFApp.Rentals"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="RentalsUserControl">
<Grid x:Name="rootGrid" Background="White" >
<StackPanel>
<TextBlock Text="VertragsNr"/>
<TextBox x:Name="VertragsNr" Text="{Binding ElementName=collectionNavigator2, Path=CurrentItem.VertragsNr}" />
</StackPanel>
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
x:Name="collectionNavigator2"
Source="{Binding Rentals}" />
</Grid>
Essentially what I want to do is Bind the DataContext of UserControl2 (Rentals) to the CurrentItem of collectionNavigator in UserControl1 (Addresses)
But I can't figure out how. I tried many things, with Binding Path and RelativeSource but none seem to work. the only way I could get it to work was to copy the content of UserControl1 into my MainWindow.xaml, but then I can't reuse it.
Any help is much appreciated.
UPDATE >>>>>
Working solution from Sheridan.
MainWindows
...
<local:RentalCustomer x:Name="rentalCustomer" Height="400" />
<local:Rentals Height="100" DataContext="{Binding CurrentAddress, ElementName=rentalCustomer}" />
RentalCustomer.xaml
...
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
Grid.ColumnSpan="2"
x:Name="collectionNavigator"
Source="{Binding Addresses}"
CurrentItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=CurrentAddress, Mode=OneWayToSource}"
/>
RentalCustomer.xaml.cs
public Address CurrentAddress
{
get { return (Address)GetValue(CurrentAddressProperty); }
set { SetValue(CurrentAddressProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentAddress. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentAddressProperty =
DependencyProperty.Register("CurrentAddress", typeof(Address), typeof(RentalCustomer), new PropertyMetadata(null));

You can data bind the current item of a collection using the "/" Binding notation. Try something like this:
<UserControl Name="Control1" DataContext="{Binding SomeCollection}" />
<UserControl Name="Control2" DataContext="{Binding SomeCollection/}" />
Binding SomeCollection/ means bind to the current item of the SomeCollection collection. To make this work, you may need to set the Selector.IsSynchronizedWithCurrentItem Property to True on any container controls that you are selecting the current item in:
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ... />
UPDATE >>>
Without an IsSynchronizedWithCurrentItem property, the "/" Binding notation won't work. Instead, you could expose the selected item from the collection as a DependencyProperty on the relevant UserControl. Then you could do something like this:
<UserControl Name="Control2" DataContext="{Binding SelectedItem, ElementName=Control1}" />

You could provide the Rentals from your viewModel by binding the CurrentItem to your viewModel and expose the Rentals like this:
<telerik:RadCollectionNavigator CurrentItem={Binding CurrentItem} />
public Address CurrentItem
{
get; set;
}
public IEnumerable<Rentals> Rentals
{
get { return CurrentItem.Rentals; }
}

Related

Why tabitem do not show view

I am workign on MVVM, and i have Main View in which i have a tabcontrol and 3 tabitems. Now on click to each tabitems i want to display a new view. (I have three Views).
My try to do that is :
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm1="clr-namespace:WpfApplication1"
xmlns:vm2="clr-namespace:WpfApplication1"
xmlns:vm3="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm1:View1 x:Key="View1Display1"></vm1:View1>
<vm2:View2 x:Key="ViewDisplay2"></vm2:View2>
<vm3:View3 x:Key="ViewDisplay3"></vm3:View3>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<TabControl Background="Green">
<TabItem Height="20" Width="100" Header="Tab1" DataContext="{Binding Path=View1Display1}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab2" DataContext="{Binding Path=View1Display2}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab3" DataContext="{Binding Path=View1Display3}"></TabItem>
</TabControl>
</Grid>
</Window>
Where View1 is : (Similar is View2 and View3)
<UserControl x:Class="WpfApplication1.View1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">I am from View1</TextBlock>
</Grid>
</UserControl>
MainWindow.xaml.cs is :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
This is ViewModel:
class ViewModel
{
public ViewModel()
{
}
}
Why TabItem do not show the respective views on clicking to them even i have set datacontext corresponding to their View.
Instead of binding the DataContext on each TabItem, you need to bind the Content, and then you can bind each TabItem to an instance of its ViewModel, of Do that in the UserControls them selfs
<TabControl Background="Green">
<TabItem Height="20" Width="100" Header="Tab1" Content="{StaticResource View1Display1}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab2" Content="{StaticResource ViewDisplay2}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab3" Content="{StaticResource ViewDisplay3}"></TabItem>
</TabControl>
since your views are defined as static resource use StaticResource instead of binding

XAML Reuse a block of markup

Consider I have multiple pages and I want to show the same "Header" in every page without copying & pasting it multiple times: it's very unmaintainable.
<!-- #region Header -->
<Grid Grid.Row="0">
<Rectangle Style="{StaticResource HeaderBackground}" />
<TextBlock
Style="{StaticResource PageTitle}"
Text="Page1" />
<Button
Content="Settings"
Command="{Binding GotoSettingsPage}" />
</Grid>
<!-- #endregion Header -->
What are the options to make this block of markup reusable? For example it would be nice to write something like this:
<MyHeader Grid.Row="0" PageTitle="Page1" />
A user control is an option.
MyHeader.Xaml:
<UserControl
x:Class="App1.MyHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Rectangle Style="{StaticResource HeaderBackground}" />
<TextBlock x:Name="Title"
Style="{StaticResource PageTitleStyle}"
Text="" />
<Button Content="Settings"
Command="{Binding GotoSettingsPage}"/>
</Grid>
</UserControl>
MyHeader.Xaml.cs:
using Windows.UI.Xaml.Controls;
namespace App1
{
public sealed partial class MyHeader : UserControl
{
public MyHeader()
{
this.InitializeComponent();
}
public string PageTitle
{
get { return Title.Text; }
set { Title.Text = value ?? ""; }
}
}
}
MainPage.Xaml:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:MyHeader PageTitle="Page 1"/>
</Grid>
</Page>

Accessing/Setting Custom Text Properties of codebehind through XAML

Having some trouble getting my head around all the DP's i've recreated a very simple version of of my problem to try and sort out what the issue is. Alas, im still stumped..
I Have a custom userControl, and am attempting to set the properties of a textblock through the page that is instantiating that control. At the moment, in this version below, as a means of teaching myself im only trying to access a property from code behind.
The reason I wish to set custom text properties etc. is because I wish to have multiple versions of the control on the page with custom behaviour/text etc.
Here is my code behind.
public partial class FeedbackCtrl : UserControl
{
public bool IsFlagSet
{
get { return (bool)GetValue(IsFlagSetProperty); }
set { SetValue(IsFlagSetProperty, value); }
}
public static readonly DependencyProperty IsFlagSetProperty =
DependencyProperty.Register("IsFlagSet", typeof(bool), typeof(FeedbackCtrl), new PropertyMetadata(false));
public FeedbackCtrl()
{
InitializeComponent();
this.DataContext = this;
}
}
And my XAML..
<UserControl x:Class="Pipeline_General.Custom_Controls.FeedbackCtrl" x:Name="ThisCtrl"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Expander HorizontalAlignment="Stretch" >
<Expander.Header Name="Header">
<Grid HorizontalAlignment="Stretch" Width="NaN">
<Border Background="Transparent" HorizontalAlignment="Stretch" BorderBrush="gray" BorderThickness="0,0,0,1"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=ActualWidth}">
<TextBlock Foreground="#FFFF72D2" Text="{Binding Path=IsFlagSet}">
</TextBlock>
</Border>
</Grid>
</Expander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="1">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="5"/>
</ScrollViewer>
</Grid>
</Expander>
</Grid>

Binding Two ComboBoxs to the Same Source WPF

I have the following XAML where I have two combobox controls with the same ItemsScource. I used the IsSynchronizedWithCurrentItem=False to attempt to get them to act individually. But still changing one changes the other.
XAML
<Page x:Class="HaMmeR.FantasyFootball.TradeEvalPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="TradeEvalPage">
<Grid x:Name="masterPanel">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" Style="{DynamicResource dockLsLabel}">
<Label x:Name="teamName1Lbl" Content="Team Name" Style="{DynamicResource lsLabel}" />
<!--IsSynchronizedWithCurrentItem=False Treat datasource as individual-->
<ComboBox x:Name="teamName1Cmb" Margin="2" DockPanel.Dock="Right"
ItemsSource="{Binding Path=FantasyTeams}"
IsSynchronizedWithCurrentItem="False"
DisplayMemberPath="TeamName"
SelectionChanged="teamName1Cmb_SelectionChanged"
SelectedValuePath="TeamName" SelectedValue="{Binding Path=TeamName}"/>
</DockPanel>
<DockPanel Grid.Row="0" Grid.Column="1" Style="{DynamicResource dockLsLabel}">
<Label x:Name="teamName2Lbl" Content="Team Name" Style="{DynamicResource lsLabel}" />
<ComboBox x:Name="teamName2Cmb" Margin="2" DockPanel.Dock="Right"
ItemsSource="{Binding Path=FantasyTeams}"
IsSynchronizedWithCurrentItem="False"
DisplayMemberPath="TeamName"
SelectionChanged="teamName2Cmb_SelectionChanged"
SelectedValuePath="TeamName" SelectedValue="{Binding Path=TeamName}"/>
</DockPanel>
</Grid>
</Page>
.cs File
public partial class TradeEvalPage : Page
{
private FantasyLeague league;
public TradeEvalPage(FantasyLeague League)
{
InitializeComponent();
league = League;
//FantasyTeamCollection team1Source = new FantasyTeamCollection(league.FantasyTeams.ToList<FantasyTeam>());
//FantasyTeamCollection team2Source = new FantasyTeamCollection(league.FantasyTeams.ToList<FantasyTeam>());
//teamName1Cmb.DataContext = team1Source;
//teamName2Cmb.DataContext = team2Source;
masterPanel.DataContext = league;
//mainContent.Text = string.Format("You clicked Trade Evalution of {0}", league.LeagueName);
}
}
I can't figure out what I'm doing wrong. Any help would be appreciated.
I think because you Bind the selectedValue to the TeamName.
SelectedValue="{Binding Path=TeamName}"
If you remove the bind, they probably work. I think whats happen here is, when you change the selection in one combobox, the value of the new selected item is written in the current items-name. So it looks that the first combobox does the same, but if you open it, you'll see that value twice.

Caliburn Micro: UserControl Binding

I use Caliburn Micro for my WPF application. I implemented a little UserControl:
<UserControl Name="ImageButtonUserControl"
x:Class="SportyMate.Utility.Controls.ImageButton"
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">
<Grid>
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ElementName=ImageButtonUserControl, Path=Image}" />
<TextBlock Text="{Binding ElementName=ImageButtonUserControl, Path=Text}" />
</StackPanel>
</Button>
</Grid>
</UserControl>
Now I want to use these Control in my view:
<uc:ImageButton Name="Cancel" Image="/Images/Icons/cancel_16x16.png" Text="Abbrechen" Margin="3" />
When I want to open my view (in my case it's opened as a dialog) it doesn't work. The View does not openend.
When I remove the Name-Attribute everthing is fine, but the Button have no binding to an action. Can anyone tell me what I have to do for a correct binding? A regular Button worked.
You are on a completely wrong way. You shouldn't create a user control for such a simple modification. You need to create a DataTemplate and use it for a Button.ContentTemplate. First you need to define a helper type for button content:
public class ImageButtonContent
{
public BitmapImage Image { get; set; }
public string Label { get; set; }
}
After that you can use it for DataTemplate:
<Window x:Class="Trys.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Trys="clr-namespace:Trys"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<Trys:ImageButtonContent x:Key="YourImageButtonHere"
Label="Your ImageButtonHere">
<Trys:ImageButtonContent.Image>
<BitmapImage UriSource="your-icon.png" />
</Trys:ImageButtonContent.Image>
</Trys:ImageButtonContent>
<DataTemplate x:Key="ImageButton"
DataType="{x:Type Trys:ImageButtonContent}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}"
Margin="5" />
<TextBlock Text="{Binding Label}"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button ContentTemplate="{StaticResource ImageButton}"
Content="{StaticResource YourImageButtonHere}"
Height="50"
Width="250" />
</Grid>
</Window>
I used a resource for an object, but you can use a property on your ViewModel. The result is:
And this just a normal button. You can use for it all power of Caliburn.Micro conventions like Click event default binding. Enjoy!

Categories

Resources