Say I have these two User Controls. How would I pass data entered in the TextBox from ControlOneto the TextBox in ControlTwo when the Button in ControlOne is clicked?
<UserControl x:Class="Project.ControlOne"
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>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="MyTextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" />
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Send" Click="Button_Click" />
</StackPanel>
</Grid>
</UserControl>
...
namespace Project
{
public partial class ControlOne : UserControl
{
public ControlOne()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
<UserControl x:Class="Project.ControlTwo"
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>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="MyTextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" />
</StackPanel>
</Grid>
</UserControl>
Your two user controls should have no knowledge of each other (unless one contains the other). That's why you can't "pass data" between them. The idea behind a user control is that you can drop it anywhere as many times as needed. If ControlOne knew about ControlTwo, what would happen if you used them separately? Or had three ControlTwos in the same place?
The layer which contains both of them should be the one to read a value from one and set it to the other. If you want it to happen on the button press, you should look into event handling so the parent can know when the control's button is pressed.
Related
There are two Buttons, one for save and one for load (WPF)
I'd like to save all the XAML Code of the Program
, and be able to load it back to its saved State
The Canvas i want to save has a lot of objects in it and don't like to hardcode everything.
Is there a way to do this for all cases and not only for this one?
<Window x:Class="test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test"
mc:Ignorable="d"
Title="test" Height="552" Width="1143">
<Canvas Name="cs">
<Canvas Name="csmain" HorizontalAlignment="Left" Height="523" VerticalAlignment="Top" Width="1129">
<TextBox Name="tbx" Height="23" Canvas.Left="173" Text="" Canvas.Top="10" Width="159" />
</Canvas>
</Canvas>
</Window>
void save(string path)
{
???
}
void load(string path)
{
???
}
Learning C#, specifically WPF, and the MVVM framework. I'm creating a basic project that presents a MainWindow with a contentcontrol binding. Straightforward.
I have 2 views, each with a textbox. I have 2 buttons on the MainWindow, each allow me to toggle between views. However, when I enter data in a textbox, switch views, and come back, the data is gone. How can I persist that data to be consumed later?
Relevant code:
MainWindow.xaml
<Window x:Class="TestDataRetention.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestDataRetention"
xmlns:views="clr-namespace:TestDataRetention.Views"
xmlns:viewmodels="clr-namespace:TestDataRetention.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type viewmodels:View1ViewModel}">
<views:View1View DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:View2ViewModel}">
<views:View2View DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Right">
<Button x:Name="View1Button" Margin="10" Width="80" Content="View1" Click="View1Button_Click"/>
<Button x:Name="View2Button" Margin="10" Width="80" Content="View2" Click="View2Button_Click"/>
</StackPanel>
<ContentControl x:Name="Content" Grid.Row="0" Content="{Binding}"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
using TestDataRetention.ViewModels;
namespace TestDataRetention
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void View1Button_Click(object sender, RoutedEventArgs e)
{
DataContext = new View1ViewModel();
}
private void View2Button_Click(object sender, RoutedEventArgs e)
{
DataContext = new View2ViewModel();
}
}
}
View1View.xaml
<UserControl x:Class="TestDataRetention.Views.View1View"
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:local="clr-namespace:TestDataRetention.Views"
xmlns:vm="clr-namespace:TestDataRetention.ViewModels"
mc:Ignorable="d"
FontSize="24"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:View1ViewModel/>
</UserControl.DataContext>
<Grid Background="AliceBlue">
<StackPanel>
<Label HorizontalAlignment="Center" Content="Enter View1 Stuff"/>
<TextBox x:Name="View1TextBox" Width="400" Height="50" Text="{Binding View1Words}" />
</StackPanel>
</Grid>
</UserControl>
View1View.xaml.cs
using System.Windows.Controls;
using TestDataRetention.ViewModels;
namespace TestDataRetention.Views
{
public partial class View1View : UserControl
{
public View1View()
{
InitializeComponent();
this.DataContext = new View1ViewModel();
}
}
}
View2 is obviously the same as View1 but with corresponding variables.
While there might also be a way to just have wpf cache it for you, you can just as easily save it properly and then have the input available at will.
Look here for two methods on how to:
How to save user inputed value in TextBox? (WPF, XAML)
At quick glance, you create new ViewModel each time your button is clicked, this will always create new ViewModel for your DataContext not using the original one.
Also this snippet from your code will create new ViewModel for your Control's DataContext regardless of the one the parent control has:
<UserControl.DataContext>
<vm:View1ViewModel/>
</UserControl.DataContext>
And I am not sure how you use your DataTemplate here:
<DataTemplate DataType="{x:Type viewmodels:View1ViewModel}">
<views:View1View DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:View2ViewModel}">
<views:View2View DataContext="{Binding}"/>
</DataTemplate>
But generally, as a guidance for you to model your MVVM project, always keep in mind that XAML code is translated to C# while compiling. So when writing something like <vm:View1ViewModel/> you actually do new View1ViewModel().
So for you to use the DataContext your control inherited from its parent, you just use <UserControl DataContext="{binding}" for your UserControl.
And for your button click, you have to keep a pointer for your previously created ViewModel and assign it to the DataContext when needed, I suggest you to create these ViewModels only when needed to minimize memory consumption in large applications, like:
private View1ViewModel m_View1VM = null;
private void View1Button_Click(object sender, RoutedEventArgs e)
{
if (m_View1VM is null)
m_View1VM = new View1ViewModel();
this.DataContext = m_View1VM;
}
I have a small UI in XAML where I need to display twice the same thing in the same window. I created a Resources with the code, but can't figure out how to display it.
the resources :
<max:MaxUserControl.Resources>
<DataTemplate x:Key="tInfo">
<max:MaxGrid>
<max:MaxGrid.ColumnDefinitions>
...
</max:MaxGrid.ColumnDefinitions>
<max:MaxGrid.RowDefinitions>
...
</max:MaxGrid.RowDefinitions>
...
</max:MaxGrid>
</DataTemplate>
</max:MaxUserControl.Resources>
The only difference between both UI is the Datacontext, so I wanted to do something like :
<max:MaxStackPanel Grid.Row="1" Grid.Column="0" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding ElementName=dtgEmployeeOccupation, Path=SelectedItem, Mode=OneWay}"/>
<max:MaxStackPanel Grid.Row="0" Grid.Column="1" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding Path=ANOTHERBINDING"/>
What control I should use to achieve this?
Create UserControl and reuse where you want. At first you should create
UserControl, then add some necessary controls inside your UserControl. For example, we are creating UserControl and it would be called FooUserControl:
<UserControl x:Class="OpenExcelFileAndConvertToArray.FooUserControl"
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:local="clr-namespace:OpenExcelFileAndConvertToArray"
mc:Ignorable="d">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="SomeText"/>
<Button Content="Delete"/>
</StackPanel>
</Grid>
</UserControl>
Then just in any other controls you can reuse this FooUserControl. For example:
<Window x:Class="OpenExcelFileAndConvertToArray.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OpenExcelFileAndConvertToArray"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox Text="qq" Name="comboBox">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="3"/>
</ComboBox>
<!--reusable control-->
<local:FooUserControl/>
</StackPanel>
</Grid>
I have a modern window in WPF/C# application, in which I added a modern frame:
<mui:ModernWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
WindowStartupLocation="CenterScreen"
Style="{StaticResource EmptyWindow}">
<Window.Resources>
</Window.Resources>
<Grid>
<Menu x:Name="menu" Height="62" VerticalAlignment="Top" >
<MenuItem x:Name="miHome" Header="Home" Click="MenuItem_Home" IsChecked="True" Width="60" FontSize="14" />
<MenuItem x:Name="miClients" Header="Clients" FontSize="14" Click="MenuItem_Clients" Width="65"/>
<MenuItem x:Name="miSuppliers" Header="Suppliers" FontSize="14" Click="MenuItem_Suppliers" Width="81"/>
<MenuItem x:Name="miReports" Header="Reports" FontSize="14" Click="MenuItem_Reporting" Width="71"/>
</Menu>
<mui:ModernFrame Margin="0,75,10,10" x:Name="frame">
</mui:ModernFrame>
</Grid>
I have MenuItems in my application, when I click on Suppliers item, I fill the frame with a usercontrol, like this:
frame.Source = new Uri("/Pages/Suppliers.xaml", UriKind.Relative);
Where Suppliers.xaml design is:
<UserControl
x:Class="MyApp.LinksBar.Suppliers"
xmlns:MyApp="clr-namespace:MyApp"
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:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="575" d:DesignWidth="905">
<UserControl.Resources>
</UserControl.Resources>
<Grid Name="Grid">
<mui:ModernButton x:Name="btnmakePayment" Content="Make Payment" Click="btnMakePayment_Click" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
</UserControl>
When I click on "Make Payment" button, I navigate to another UserControl (MakePayment.xaml):
private void btnMakePayment_Click(object sender, RoutedEventArgs e)
{
NavigationCommands.GoToPage.Execute(new Uri("/Actions/MakePayment.xaml", UriKind.Relative), this);
}
MakePayment.xaml design is:
<UserControl
xmlns:local="clr-namespace:MyApp.Actions" x:Class="MyApp.Actions.MakePayment"
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:mui="http://firstfloorsoftware.com/ModernUI" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:MyApp="clr-namespace:MyApp"
mc:Ignorable="d"
Loaded="MakePayment_Loaded"
d:DesignHeight="600" d:DesignWidth="866" >
<UserControl.Resources>
</UserControl.Resources>
<Grid DataContext="{StaticResource makePaymentViewSource}" Name="Grid">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Label Content="Total" VerticalContentAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Top"/>
// More design code here ...
</Grid>
</UserControl>
Here comes my question:
I need to pass parameters from Suppliers UserControl to MakePayment UserControl.
How to programmatically pass the parameters in Suppliers and read them in MakePayment?
Thank you.
If you can bind one to the other, that is what you should be doing. By far, the easiest way to make two UserControls "communicate" (or share properties that both can do stuff with) is to define a DependencyProperty on each and bind them two-way.
This way, both always have access to the same value and both can do stuff with it.
Take my own control as an example:
<UserControl x:Class="MyControls.MasterContainerControl"
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:local="clr-namespace:MyControls"
mc:Ignorable="d"
x:Name="masterContainerControl">
<local:ContainerControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty}">
<local:ContainerControl.Another>
<local:AnotherControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ExplorerBase.AddressBar>
<local:ContainerControl.Some>
<local:SomeControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ContainerControl.Some>
</local:ContainerControl>
</UserControl>
This, of course, all assumes MasterContainerControl, ContainerControl, AnotherControl, and SomeControl all have a DependencyProperty called SomeProperty, and then the bindings seal the deal.
Note: Make sure the default values are defined in MasterContainerControl because those will override the values MasterContainerControl binds to.
If I misunderstood your issue, please let me know.
I have the following dummy application where I'm trying to build a Master Detail with two views. The first is the collection view, which I can successfully select an element of and it displays in the Content Presenter Data Template with the TextBlock and TextBox defined as they are below.
I have tried to Move the TextBlock and TextBox out to a view, but have been unsuccessful at getting it to display the data. If I remove the TBs and uncomment the view, it will display the view but the TBs in the view won't populate.
Of course, the idea is that I will have more than one type.
MainWindow
<Window x:Class="MyApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:MyApp.Views"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<views:CollectionView DataContext="{Binding myItemCollection}">
</views:CollectionView>
<ContentPresenter x:Name="Detail" Content="{Binding myItemCollection.SelectedViewModel}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type viewModel:TextViewModel}">
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
<!--<views:TextView/>-->
</StackPanel>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
</Grid>
TextView
<UserControl x:Class="MyApp.Views.TextView"
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:local="clr-namespace:MyApp.Views"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Text Item"/>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
</StackPanel>
</Grid>
You need to remove the prism:ViewModelLocator.AutoWireViewModel="True" attribute from your TextView.
What it does is it pulls the appropriate view model from the container and assigns the view model to TextView.DataContext. On the other hand in your template you do not explicitly pass the templated data to the TextView control, so it's expected to be inherited via automatic inheritance of DataContext. But that does not work, because TextView.DataContext is explicitly set by prism:ViewModelLocator.AutoWireViewModel="True".
In case it is required to use view model auto wiring you can always set this attribute from referencing scope, that is in XAML in which your view is "used", e.g.:
<StackPanel>
<views:TextView prism:ViewModelLocator.AutoWireViewModel="True" />
</StackPanel>