I'm implementing a class that contains an ObservableCollection and in my XAML I have a polyline.
I successfully binded the XAML dataContext to the this class and the polyline to the Observable collection. But now, of course, I'm facing a wrong type conversion.
I found an example of value converter and I added it to my code but I'm not able to add it as a resource in my XAML ...
The overall structure looks like that
public class externalClass
{
public ObservableCollection<Point> debugCh1 { get; set; }
public void test()
{
... performo modifications
on debugCh1 for testing purposes...
}
public class PointCollectionConverter : IValueConverter
{
.. implements convert and cnverBack
}
}
For the XAML
<Window x:Class="tester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="277" Width="525" xmlns:my="clr-namespace:binding;assembly=binding" xmlns:my1="clr-namespace:deviceManager;assembly=deviceManager" Closing="Window_Closing">
<Window.Resources>
<local:PointCollectionConverter x:Key="pointCollectionConverter"/>
</Window.Resources>
... The window Itself ...
<Polyline Points="{Binding debugCh1}" />
...
</Window>
And for the C# behind
public partial class MainWindow : Window
{
private externalClass toTest;
public MainWindow()
{
InitializeComponent();
DataContext = toTest;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
toTest.test();
}
}
The whole thing works nice but for the <local:PointCollectionConverter..
Compiler says The type 'local:PointCollectionConverter' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
Any suggestions on how to add this reference??
You just have to make sure the namespaces are lined up. I'd start by separating your converter class from the externalClass class (I'm not sure it's even possible to reference nested-classes like that from XAML):
namespace MyCompany.MyProject
{
public class PointCollectionConverter : IValueConverter
{
.. implements convert and cnverBack
}
}
Now you can define the local xmlns and link it to MyCompany.MyProject:
<Window xmlns:local="clr-namespace:MyCompany.MyProject"
And with that the converter should be accessible as written.
<local:PointCollectionConverter x:Key="pointCollectionConverter"/>
Related
I'm creating a UserControl in WPF, that is able to work for any object of type IMyNode. Basically, it receives an ObservableCollection through a dependency property, register to it and do some stuff.
In one of my usecase, I use in a control that uses(and need), an ObservableCollection of SomeSpecificNode. SomeSpecificNode is an implementation of IMyNode.
Currently, I've a binding error:
System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'System.Collections.ObjectModel.ObservableCollection`1[SomeSpecificNode]' and 'System.Collections.ObjectModel.ObservableCollection`1[IMyNode]'.
I understand why it happens, it doesn't know how to convert automatically an ObservableCollection<SomeSpecificNode> to ObservableCollection<IMyNode>.
What would be the correct approach to do this?
Using a converter would break the NotifyPropertyChange. Using a ObservableCollection<IMyNode> in my parent ViewModel would not work for the other control in the same page.
Thank you!
Here some pseudo code:
public class SomeSpecificNode: IMyNode{
}
public interface IMyNode{
}
public class ParentViewModel {
public ObservableCollection<SomeSpecificNode> SelectedNodes {get;}=> new ObservableCollection<SomeSpecificNode>()
}
<UserControl x:Class="ParentView"
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:ch.VibroMeter.Xms.Configurators.Controls.ActionBar"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<ParentViewModel/>
</UserControl.DataContext>
<StackPanel Orientation="Horizontal" Name="RootContainer">
<SomeChildControl Nodes="{Binding SelectedNodes}" /><!-- This binding will fail !-->
</StackPanel
</UserControl>
public partial class ParentView : UserControl
{
public static readonly DependencyProperty NodesProperty = DependencyProperty.Register(
nameof(Nodes), typeof(ObservableCollection<IMyNode>), typeof(ParentView), new PropertyMetadata(default(ObservableCollection<IMyNode>), OnNodesChanged));
private static void OnNodesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//...
}
public ObservableCollection<IMyNode> Nodes
{
get { return (ObservableCollection<IMyNode>)GetValue(NodesProperty); }
set { SetValue(NodesProperty, value); }
}
}
You should change the type of the dependency property to a compatible type such as IEnumerable<IMyNode>.
You cannot set an ObservableCollection<IMyNode> property to anything else than an ObservableCollection<IMyNode> or null.
An ObservableCollection<SomeSpecificNode> is not an ObservableCollection<IMyNode> but it is an IEnumerable<IMyNode> assuming that SomeSpecificNode implements IMyNode.
So this compiles just fine;
IEnumerable<IMyNode> collection = new ObservableCollection<SomeSpecificNode>();
But this doesn't:
ObservableCollection<IMyNode> collection = new ObservableCollection<SomeSpecificNode>(); //Cannot implictly convert type...
The difference is that IEnumerable<T> is covariant. Please refer to the docs for more information.
I have a Windows Phone project and my business demands to create another one with some slight changes in the front-end (XAML). How to create another project that is identical to the first one but only the XAML files are different? I don't use MVVM. What I tried is creating a new project and copy the XAML files from the first one, and then LINK all other CS files, but it became a mess with all these namespaces and stuff.. I have resource dicionaries and lots of dependencies in the code. Any ideas how to make such a project that shares the same code-behind files with some differences in Visual Studio?
XAML with a code behind are partial classes. You cannot have two partial classes referring to the same class in two different assemblies. Therefore I think you can't use common code behind for XAML from different projects.
The best approach is using a common view model for different views, but you don't use MVVM pattern.
Then you can use something like a proxy. The proxy is a common class in a separate assembly. It contains all logic and data.
You get or set any state from your code behind only by the proxy.
UPD: Example:
It's a common contract for each view (it's an interface from a common assembly):
public interface IMyWindow
{
Label HelloLabel { get; }
}
It's the first WPF project:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="First Application" Height="350" Width="525">
<StackPanel>
<Label x:Name="_helloLabel" Content ="Hello, I'm First Application!"></Label>
<Button Click="ButtonBase_OnClick" Height="100">Press me</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window, IMyWindow
{
private readonly MyWindowProxy _proxy;
public MainWindow()
{
InitializeComponent();
_proxy = new MyWindowProxy(this);
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_proxy.OnButtonClick();
}
public Label HelloLabel
{
get { return _helloLabel; }
}
}
It's the second WPF project:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Second Application" Height="350" Width="525">
<StackPanel>
<Label x:Name="_helloLabel" Content ="Hello, I'm Second Application!"></Label>
<Button Click="ButtonBase_OnClick" Width ="50" Height="50">OK</Button>
</StackPanel>
</Window>
public partial class MainWindow : Window, IMyWindow
{
private readonly MyWindowProxy _proxy;
public MainWindow()
{
InitializeComponent();
_proxy = new MyWindowProxy(this);
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_proxy.OnButtonClick();
}
public Label HelloLabel
{
get { return _helloLabel; }
}
}
It's a proxy for each view (it's a class from a common assembly):
public class MyWindowProxy
{
private readonly IMyWindow _window;
public MyWindowProxy(IMyWindow window)
{
_window = window;
}
public void OnButtonClick()
{
_window.HelloLabel.Content = "Hello from common proxy!";
}
}
Once again, this is NOT the best way to build an application architecture. I highly recommend using MVVM pattern then the question of separating of business logic disappear by itself.
All -
I am using Unity in my WPF application for DI (without prism). I have my MainWindow.xaml and MainWindowViewModel.cs. I have a usercontrol in my Mainwindow.xaml. The user control has its own uc1.xaml and uc1viewmodel.cs. The UC1 ViewModel is currently exposed as a property on MainWindowViewModel so I can set the datacontext on the usercontrol (as recommended by many ppl here).
The question I have is how/where can I set this property - will it be in app.xaml.cs or will it be in the constructor of mainwindowviewmodel. Code Snippets:
App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Step 1 - One Time - Creating an instance of the container
UnityContainer unity = new UnityContainer();
//Step 2 - Registering your MainWindowViewModel
unity.RegisterType<IViewModel, UserControl1ViewModel>();
//Step 3 - Creating an Instance
UserControl1ViewModel uc1_mwvm = unity.Resolve<UserControl1ViewModel>(); <-- doesnt help
MainWindowViewModel mwvm = unity.Resolve<MainWindowViewModel>();
MainWindow mw = unity.Resolve<MainWindow>();
mw.Show();
}
MainWindowViewModel.cs
public class MainWindowViewModel
{
public IViewModel IVM { get; set; }
public MainWindowViewModel()
{
//IVM = new UserControl1ViewModel(); <-- All I really want is an equivalent but letting Unity do the work.
}
}
MainWindow.xaml
<Window x:Class="_05_ViewFist_UC_Unity_Working.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc1="clr-namespace:_05_ViewFist_UC_Unity_Working"
xmlns:uc2="clr-namespace:_05_ViewFist_UC_Unity_Working"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding NNN}" />
<uc1:UC1 DataContext="{Binding UC1VM}" />
<uc2:UC2 DataContext="{Binding UC2VM}" />
</StackPanel>
</Window>
UC1
<UserControl x:Class="_05_ViewFist_UC_Unity_Working.UC1"
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" >
<StackPanel Orientation="Horizontal" Background="Red">
<TextBlock Text="UC1 " />
<TextBlock Text="{Binding FirstName}" />
</StackPanel>
</UserControl>
As you see from the code - Instance of UC1 is created in xaml (MainWindow.xaml) and hence when MainWindow instance is created in app.xaml.cs - it still doesnt create an instance of UserControl1ViewModel.
Question again is : Dont think its a good practice for me to call the Unity Resolve statement in the constructor of MainwindowViewModel. Is that correct??
Can somebody share a code snippet of how/where I can do this?
Thanks
I downloaded your solution from github and tried to solve your problem.
You did a great job just you forgot few details such as property attributes.
This is how your App.cs file shall look alike:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Step 1 - One Time - Creating an instance of the container
UnityContainer unity = new UnityContainer();
//Step 2 - Registeration
unity.RegisterType<IMainWindowViewModel, MainWindowViewModel>();
unity.RegisterType<IUC1ViewModel, UC1ViewModel>();
unity.RegisterType<IUC2ViewModel, UC2ViewModel>();
//// Instance of MainWindowViewModel will be created once you call Resolve MainWindow.
MainWindow mw = unity.Resolve<MainWindow>();
mw.Show();
}
Here is what I changed:
public class MainWindowViewModel : IMainWindowViewModel
{
#region Public Properties
[Dependency]
public IUC1ViewModel UC1VM { get; set; }
[Dependency]
public IUC2ViewModel UC2VM { get; set; }
public string NNN { get; set; }
#endregion
public MainWindowViewModel()
{
NNN = "This value coming from MainWindowViewModel";
}
}
[Dependency] is a property attibute that tells Unity where to inject values.
I could merge my code to your repo in github if you wish so.
Let me know if this helped you any futher. Feel free to mark this as the answer.
You can use the service locator pattern. I use it with Unity as a DI.
internal class ServiceLocator
{
[...]
public MainViewModel Main { get { return container.Resolve<MainViewModel>(); } }
}
You can intantiate your class the way you want (DI or not, the class initializes the DI or receive it as a parameter, you can store the DI in a private static property, you can initialize your class if DI is null or when the application starts etc...).
In your App.xaml
<Application.Resources>
<vm:ServiceLocator x:Key="Locator"/>
</Application.Resources>
And now, you can set your datacontext
DataContext="{Binding Main, Source={StaticResource Locator}}"
Edit:
I found another way of doing it (among other):
Take a look at this article. In the command, you can resolve your viewmodel as you like.
I am a beginner in WPF and c#.I am trying to create an application using Kinect for Windows. I have a kinectsensorchooser in my Kinect.cs and it controls the SensorChooser in the MainWindow.xaml
But I am not sure how to control it.
My code is as follows:
MainWindow.xaml
<Canvas>
<k:KinectSensorChooserUI KinectSensorChooser="{Binding SCkinectSensorChooser} "Name="sensorChooserCP"/>
<k:KinectUserViewer k:KinectRegion.KinectRegion="{Binding kinectRegionCP}"/>
<k:KinectRegion Name="kinectRegionCP" KinectSensor="{Binding ElementName=SCkinectsensor}">
<Grid>
some kinect tile buttons come in here...
</Grid>
</k:KinectRegion>
</Canvas>
Kinect.cs
public KinectSensor SCkinectsensor;
public KinectSensorChooser SCkinectSensorChooser;
the values of the two objects will be set dynamically during the program execution. I want these changes to be reflected in the MainWindow
App.xaml
<Application x:Class="Kinect.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
xmlns:local="clr-namespace:Kinect">
<Application.Resources>
<local:Kinect x:Key="Kinect" />
</Application.Resources>
I am doing something wrong and the code is not responding as I wish... What should I do? I need help
Are you setting your DataContext in the main window? If not, your Binding will do nothing.
MainWindow.xaml
<Window x:Class="Kinect.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{StaticResource Kinect}">
...
</Window>
Another thing to note, is that in WPF, you can only bind to properties.
Kinect.cs
public KinectSensor SCkinectsensor { get; private set; }
public KinectSensorChooser SCkinectSensorChooser { get; private set; }
If you expect these properties to change outside of the constructor, then this class needs to implement INotifyPropertyChanged, and your properties would look like this:
private KinectSensor kinectSensor;
public KinectSensor SCkinectSensor
{
get { return kinectSensor; }
set
{
kinectSensor = value;
PropertyChanged(this, new PropertyChangedEventArgs("SCkinectSensor");
}
}
Pretty simple task, but the source code doesn't do required job... Please advise.
There is Products collection in the class (approach is based on the MVVm pattern, but that is not influe on the current issue):
public class ProductWindowViewModel : WorkspaceViewModel // implements INotifyPropertyChanged
{
public ProductWindowViewModel()
{
Products = new List<Product>(ProductService.Instance.Repository.GetAll());
}
List<Product> Products { get; set; }
}
Here is class declaration:
public class Product : IEntity
{
#region Public Properties
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Cost { get; set; }
#endregion
}
The class instance is binded to the window's Grid data context:
ProductWindow wnd = new ProductWindow();
wnd.MainGrid.DataContext = new ProductWindowViewModel();
wnd.ShowDialog();
And here is xaml code of the window:
<Window x:Class="WpfTest1.ProductWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProductWindow" Height="300" Width="300" xmlns:igDP="http://infragistics.com/DataPresenter"
xmlns:ViewModel="clr-namespace:ViewModel;assembly=ViewModel">
<Grid x:Name="MainGrid">
<Grid.Resources>
<ObjectDataProvider x:Key="odpObjectDataProvider" ObjectType="{x:Type ViewModel:ProductWindowViewModel}" />
</Grid.Resources>
<Grid DataContext="{StaticResource odpObjectDataProvider}">
<igDP:XamDataGrid DataSource="{Binding Path=Products}"/>
</Grid>
</Grid>
The xamDataGrid sampe is the same. overall code is pretty simple, but doesn't work.
Does anybody know why? Any thoughts are welcome.
How could I debug binding to resolve the problem himselft?
Thanks.
Ok, maybe this won't exactly answer your question, but it looks like you are instantiating your viewmodel class twice. Once in your code, immediately after creating your window, and once in the ObjectDataProvider thing. It will probably be easier to debug if you settle on one of them. Suggestion:
1. Comment out this line: wnd.MainGrid.DataContext = new ProductWindowViewModel();
2. Set a breakpoint inside the constructor of your viewmodel
3. Start it up and see if the breakpoint gets hit. If it does get hit, you know you are doing something right.
Also, check the output window in visual studio and see if any binding exceptions are reported there.
In my case the code in the windows constructor is redundant. Now all is working, seems like removing unnecessary assignement resolved an issue.