I'm a beginner in C# WPF programming. I'm trying to pop up a new window(dialog) to which some information is passed. The new window is to show the information on itself.
I pass some variables through the constructor of the popup window(OrderEntry), and I want the popup window to show the variables within its textblock control. Very simple task. But it's not working. I guess I did something wrong regarding the data-binding in the popup window's XAML code, but I can't figure it out. What did I do wrong? Please help.
The following is the MainWindow code-behind:
public partial class MainWindow : Window
{
public List<Order> Orders { get; set; }
public MainWindow()
{
InitializeComponent();
DataAccess da = new DataAccess();
Orders = da.GetOrders();
OrdersGrid.ItemsSource = Orders;
}
private void OrdersGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Order item = (Order)OrdersGrid.SelectedItem;
OrderEntry dialog = new OrderEntry(item);
dialog.Show();
}
}
The next is the XAML code for OrderEntry.xaml:
<StackPanel>
<TextBlock Text="{Binding Path=_CustomerName}" />
</StackPanel>
The following is the class for the new popup window:
public partial class OrderEntry : Window
{
public Order Order { get; set; }
public string _CustomerName { get; set; }
public OrderEntry(Order order)
{
InitializeComponent();
Order = order;
_CustomerName = order.CustomerName;
}
}
Here you are trying to use the code behind as the DataContext. In most of the cases we do binding using the ViewModel class. In your case, you can follow the following.
First you need to assign a name to your view OrderEntry in OrderEntry.xaml
<Window x:Class="OrderEntry"
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"
x:Name="OrderEntry1"
Title="CodeBehindDataContext" Height="450" Width="800">
Then assign the binding as follows.
<StackPanel>
<TextBlock Text="{Binding ElementName=OrderEntry1, Path=_CustomerName}" />
</StackPanel>
Now change your OrderEntry constructor like below.
public OrderEntry(Order order)
{
Order = order;
_CustomerName = order.CustomerName;
InitializeComponent();
}
Set the datacontext of order window to itself.
Something like this
public partial class OrderEntry : Window
{
public Order Order { get; set; }
public string _CustomerName { get; set; }
public OrderEntry(Order order)
{
InitializeComponent();
DataContext = this;//datacontext to itself
Order = order;
_CustomerName = order.CustomerName;
}
}
Related
C#, WPF.
I am trying to implement a UserControl with elements bound to properties in an object hierarchy. I have been using this as a reference.
I have created the following minimal example. It implements three instances of the UserControl, with the textbox in each case representing a filename. A dependency property is used to permit binding. Although it executes without errors, the textboxes are blank. They should contain "test1", "test2" and "test3". What am I missing?
Main window:
<Window x:Class="CustomControlTest.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:mycontrols="clr-namespace:MyControls"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="800">
<Grid>
<StackPanel Orientation="Vertical" DataContext="{Binding ElementName=parent}">
<mycontrols:DataFileControl x:Name="Ctrl1" FName="{Binding Path=project.File1.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl2" FName="{Binding Path=project.File2.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl3" FName="{Binding Path=project.File3.Filename}"/>
</StackPanel>
</Grid>
</Window>
namespace CustomControlTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
}
}
public class Project : INotifyPropertyChanged
{
public DataFile File1 { get; set; } = new DataFile();
public DataFile File2 { get; set; } = new DataFile();
public DataFile File3 { get; set; } = new DataFile();
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataFile : INotifyPropertyChanged
{
public string Filename { get; set; } = "";
public event PropertyChangedEventHandler PropertyChanged;
}
}
UserControl:
<UserControl x:Class="MyControls.DataFileControl"
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="40"
d:DesignWidth="800"
Name="TheCtrl">
<Grid Height="20">
<TextBox Name="filenameTextBox" Margin="5,0,5,0" Text="{Binding ElementName=TheCtrl, Path=FName, Mode=TwoWay}"/>
</Grid>
</UserControl>
namespace MyControls
{
public partial class DataFileControl : System.Windows.Controls.UserControl
{
public string FName
{
get { return (string)GetValue(FNameProperty); }
set { SetValue(FNameProperty, value); }
}
public static readonly DependencyProperty FNameProperty =
DependencyProperty.Register("FName", typeof(string), typeof(DataFileControl), new PropertyMetadata(null));
public DataFileControl()
{
InitializeComponent();
}
}
}
The expression
FName="{Binding Path=project.File1.Filename}"
requires a public property named project in the current DataContext, which you have not set. You should have noticed a data binding error message in the Output Window in Visual Studio when you debug the application.
Change it to
public Project project { get; } = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = this;
}
Alternatively, use the Project instance as DataContext (and thus make it a view model)
private readonly Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = project;
}
and change the binding expressions to
FName="{Binding File1.Filename}"
I'm trying to use a TabControl to switch between UserControls.
I could just set the content of the tabs to the usercontrols with XAML but then it will only be bound to the view and not the viewmodel.
My VM is a Caliburn.Micro Conductor and it calls ActivateItem whenever the user switches tabs. It worked fine when I only have one usercontrol, but when I created another one the first one will not load the view.
Here's some of the code I'm using:
ShellView:
<dx:ThemedWindow x:Class="PSCServiceManager.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Service Manager" WindowState="Maximized"
Height="525" Width="720">
<Grid>
<dx:DXTabControl>
<dx:DXTabItem Header="Master Teknisi">
<ContentControl x:Name="LoadMasterTechnicianView" cal:View.Model="{Binding ActiveItem}" />
</dx:DXTabItem>
<dx:DXTabItem Header="Servisan">
<ContentControl x:Name="LoadServicesView" cal:View.Model="{Binding ActiveItem}" />
</dx:DXTabItem>
</dx:DXTabControl>
</Grid>
ShellViewModel:
using Caliburn.Micro;
namespace PSCServiceManager.ViewModels
{
public class ShellViewModel : Conductor<IScreen>
{
private MasterTechnicianViewModel masterTechnicianViewModel;
private ServicesViewModel servicesViewModel;
public ShellViewModel()
{
LoadMasterTechnicianView();
}
public void LoadMasterTechnicianView()
{
ActivateItem(masterTechnicianViewModel);
}
public void LoadServicesView()
{
ActivateItem(servicesViewModel);
}
}
}
An easier/alternative way to implement this would be to create a collection of User Controls you would like to bind to the Tab Control. For example,
public interface ITabUserControl
{
string DisplayName { get; set; }
}
public class MasterTechnicianViewModel : ITabUserControl
{
public string DisplayName { get; set; } = "Master Technician";
}
public class ServicesViewModel : ITabUserControl
{
public string DisplayName { get; set; } = "Services";
}
Now in your ShellViewModel, you could create a Collection of ITabUserControl
public List<ITabUserControl> UserControls { get; set; }
public ShellViewModel()
{
UserControls = new List<ITabUserControl>();
UserControls.Add(new MasterTechnicianViewModel());
UserControls.Add(new ServicesViewModel());
}
And bind your TabControl as
<dx:DXTabControl x:Name="UserControls"/>
Now you can switch between the controls without any issues, without Activating it explicitly.
I have a small WPF application I'm working on which I have greatly simplified here to illustrate the problem I am having.
This is the code behind for the app:
namespace RadioRecordingMonitor
{
public partial class MainWindow : Window
{
private void Window_Loaded(object sender, RoutedEventArgs e)
{
findStationNumber();
findRecordingFailTop();
}
public void findStationNumber()
{
var listlength = 50;
this.DataContext = new stationAmount() { stationAmountTextData = listlength };
}
public void findRecordingFailTop()
{
var errorlenght = 20;
this.DataContext = new errorAmount() { errorAmountTextData = errorlenght };
}
public class stationAmount
{
public int stationAmountTextData { get; set; }
}
public class errorAmount
{
public int errorAmountTextData { get; set; }
}
}
}
And below is the XAML
<Window x:Class="RadioRecordingMonitor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Radio Recording Monitor" Width="300" Background="#ECF0EF" Height="300"
Loaded="Window_Loaded" >
<Grid>
<TextBlock DataContext="{Binding}" >
<Run Text="Total stations not recording:"/>
<Run Text="{Binding errorAmountTextData}"/>
<Run Text=" /"/>
<Run Text="{Binding stationAmountTextData}"/>
</TextBlock>
</Grid>
</Window>
The problem I am having is that only one of the databound elements displays at any one time. As it is only the errorAmountTextData element displays and if I remove the findRecordingFailTop(); method the stationAmountTextData element does display which tells me that my variable is being passed through to the XAML side.
Why is this happening and what can I do to fix the issue?
You are assigning an entirely different class to the DataContext each time, thereby replacing it each time you assign to it.
Instead, just have one class;
public class Amounts
{
public int stationAmountTextData { get; set; }
public int errorAmountTextData { get; set; }
}
Then, just assign that class to your DataContext
var myAmounts = new Amounts { stationAmountTextData = 123, errorAmountTextData = 456 };
this.DataContext = myAmounts;
When you change the datacontext to new errorAmount(), the datacontext for the whole page changes and will not work because errorAmountTextData is not found in errorAmount instance.
Keep both the properties in one class and set that as datacontext which should work fine..
I'm makeing an app where you have a listbox in the buttom with a logo of different websites, and a WebBrowseron the top. The idea is that when you press on a logo, the webBrowser load the corresponding page. I already make this work, but I want to remake the app with MVVM to make it better. I've made the listbox with all the logos, but I dont know how to load the URL onto the WebBrowser when I click on the logo.
Not 100% sure if this will work on Phone7 but worth a shot...
Fist off the WebBrowser Source property is not bindable as its not a DependancyProperty so you will have to make a helper class to create an AttachedProperty to help with the binding.
Then you can link your ListBox SelectedItem to the new LinkSource property using the Property that contains the actual link from your ListBoxItem.
Example:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication8"
Title="MainWindow" Height="233" Width="405" Name="UI">
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=UI}">
<ListBox x:Name="listbox" ItemsSource="{Binding Links}" Width="100" DisplayMemberPath="Name"/>
<WebBrowser local:WebBrowserHelper.LinkSource="{Binding ElementName=listbox, Path=SelectedItem.Site}" Width="200"/>
</StackPanel>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<Link> _links = new ObservableCollection<Link>();
public MainWindow()
{
InitializeComponent();
Links.Add(new Link { Name = "StackOverflow", Site = new Uri("http://stackoverflow.com/") });
Links.Add(new Link { Name = "Google", Site = new Uri("http://www.google.com/") });
}
public ObservableCollection<Link> Links
{
get { return _links; }
set { _links = value; }
}
}
// ListBox item
public class Link
{
public string Name { get; set; }
public Uri Site { get; set; }
}
// helper calss to create AttachedProperty
public static class WebBrowserHelper
{
public static readonly DependencyProperty LinkSourceProperty =
DependencyProperty.RegisterAttached("LinkSource", typeof(string), typeof(WebBrowserHelper), new UIPropertyMetadata(null, LinkSourcePropertyChanged));
public static string GetLinkSource(DependencyObject obj)
{
return (string)obj.GetValue(LinkSourceProperty);
}
public static void SetLinkSource(DependencyObject obj, string value)
{
obj.SetValue(LinkSourceProperty, value);
}
// When link changed navigate to site.
public static void LinkSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = uri != null ? new Uri(uri) : null;
}
}
}
Result:
Your question are actually two questions.
There're several ways to pick up clicks from listbox. Most basic one is <Listbox SelectedItem="{Binding selectedItem,mode=TwoWay}" ...
To set the web browser's URL, you could implement INotifyPropertyChanged in your VM, declare public Uri browserUri { get; private set; } in your WM, be sure to raise PropertyChanged when you change the property, and in XAML <WebBrowser Source="{Binding browserUri}" />
I am a WPF newcomer, and I've been searching for two days with no luck. I have a WPF window that has several text box controls, and a single object with some properties. This object is passed to the codebehind of my WPF window in it's constructor:
public partial class SettingsDialog : Window
{
public SettingsObject AppSettings
{
get;
set;
}
public SettingsDialog(SettingsObject settings)
{
this.AppSettings = settings;
InitializeComponent();
}
}
The SettingsObject looks something like this (simplified for clarity):
public class SettingsObject
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
public string Setting3 { get; set; }
public SettingsObject()
{
this.Setting1 = "ABC";
this.Setting2 = "DEF";
this.Setting3 = "GHI";
}
}
And my WPF window (simplified):
<Window x:Class="MyProgram.SettingsDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source=AppSettings}">
<Grid>
<TextBox Name="Setting1Textbox" Text="{Binding Path=Setting1}"></TextBox>
<TextBox Name="Setting2Textbox" Text="{Binding Path=Setting2}"></TextBox>
<TextBox Name="Setting3Textbox" Text="{Binding Path=Setting3}"></TextBox>
</Grid>
</Window>
How do you acheive two-way binding in this situation? I've tried what you see above (and so much more) but nothing works!
Have you set the DataContext property of the window to your instance of AppSettings?
public SettingsDialog(SettingsObject settings)
{
InitializeComponent();
//While this line should work above InitializeComponent,
// it's a good idea to put your code afterwards.
this.AppSettings = settings;
//This hooks up the windows data source to your object.
this.DataContext = settings;
}