Hi I am using wpf mvvm approach in my app.
I am getting this error:-
Specified element is already the logical child of another element. Disconnect it first. WPF
I have written code like this:-
In my xaml page :
<Border Background="White" BorderThickness="0" x:Name="bdrPdf">
<ContentControl x:Name="CntControlPdf"
MouseDown="Img_MouseDownPdf"
MouseMove="Img_MouseMovePdf"
MouseUp="Img_MouseUpPdf"
Width = "{Binding Path=ViewPageWidth}"
Height = "{Binding Path=ViewPageHeight}"
Content="{Binding Path=PDFViewWPFSource,ElementName=root}" >
</ContentControl>
</Border>
In Code behind :-
public pdftron.PDF.PDFViewWPF PDFViewWPFSource
{
get { return (pdftron.PDF.PDFViewWPF)GetValue(ImgSourcePropertyPDF); }
set { SetValue(ImgSourcePropertyPDF, value); }
}
// Using a DependencyProperty as the backing store for PDFViewWPFSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImgSourcePropertyPDF =
DependencyProperty.Register("PDFViewWPFSource", typeof(pdftron.PDF.PDFViewWPF), typeof(PageView), new UIPropertyMetadata(default(pdftron.PDF.PDFViewWPF)));
I am behindinf PDFtron control dynamically.
How can I solve this problem.
Where I have to written the code to de-attach this element.
Your error simply means that in WPF, you cannot display the same UI element more than once at the same time. You have two choices... either you can remove one from the UI before adding it somewhere else, or you can fake it.
What I mean by faking it, is that you can recreate the UI element elsewhere, not actually duplicating the control. You can do that either by creating a new element of the same type to use instead, or you could display your UI element via a DataTemplate and ContentControl. Then you could just provide WPF with a new object of the same type with the same values and WPF will render it in the same way as the first, making it appear as if you had duplicated it.
UPDATE >>>
thnks Sheridan, but I have to bind the ui element Like a data binding. How can I do this?I have add multiple control at runtime
In WPF, we don't data bind UI elements. Instead, we declare a data object (class) that contains the data for the element and then define a DataTemplate that will render the UI element. We then use the type of the data object as the data bound property. Take this simple example:
<DataTemplate DataType="{x:Type YourLocalPrefix:YourDataType}">
<YourUiPrefix:PDFViewWPF DataContext="{Binding}" />
</DataTemplate>
Then your property should look like this:
public YourDataType PDFViewWPFSource
{
get { return (YourDataType)GetValue(ImgSourcePropertyPDF); }
set { SetValue(ImgSourcePropertyPDF, value); }
}
Finally, display your UI element in a ContentControl:
<ContentControl Content="{Binding PDFViewWPFSource}" />
The WPF framework will see the object of type YourDataType, find the relevant DataTemplate and render your control in place of the ContentControl.
Related
I am writing a WPF application in C#. This application is design using MVVM.
Currently, I have a parent window with a few check boxes. Use user can check whichever boxes they want and then click the "plot" Button. Once they click "plot", a new child window comes up displaying the data on a single graph.
So, if I have only 1 check box checked, and then click "plot", I will see a graph with a single line on it. If I have 2 check boxes check and click "plot", I will see the same single graph, but it will have 2 lines on it.
My current Implementation:
Currently, I have a "view" class called GraphWindowView. The view obviously needs to know of which data to show. So to do that, I have dependency properties GraphWindowView.Dates and GraphWindowView.Data which ultimatley produces a graph of Data (y axis) vs. Dates (x axis).
Question: This current implementation of GraphWindowView is obviously restricted to only being able to graph one set of data (i.e. Data vs. Dates). I would like to make this (a lot) more extensible and have an arbitrary number of plots available depending on how much check boxes are checked. How would I go about doing this? I think I need to rethink my use of dependency properties...
>>> UPDATE
So I made a GraphLine class which should represent a line on the graph. The "graph" is actually a ChartPlotter element in the GraphWindowPresenter.xaml class. Additionally, I specified a DataType for the GraphLine objects, but that is all I understand. What are the next steps to this, how do I actually add the data to the graph? And how/where do I make instances of GraphLine to populate the ChartPlotter element? Sorry I am pretty lost on this, even after reading quite a few tutorials. Thanks for all the help so far, I really appreciate it!
GraphWindowView.xaml
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:ChartPlotter.HorizontalAxis>
<d3:HorizontalIntegerAxis Name="dateAxis"/>
</d3:ChartPlotter.HorizontalAxis>
<d3:ChartPlotter.VerticalAxis>
<d3:VerticalIntegerAxis Name="countAxis"/>
</d3:ChartPlotter.VerticalAxis>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=xAxis}"/>
</d3:ChartPlotter>
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:GraphLine}">
<!--WHAT GOES HERE-->
</DataTemplate>
</Window.Resources>
</Window>
GraphLine.cs
namespace BMSVM_Simulator.ViewModel
{
class GraphLine
{
public string xAxis { get; private set; }
public string yAxis { get; private set; }
public string title { get; private set; }
public string legend { get; private set; }
public EnumerableDataSource<int> data { get; private set; }
public EnumerableDataSource<int> dates { get; private set; }
}
}
Most of these types of problems in WPF can be sorted out by some careful use of data binding and DataTemplates, rather than miles of procedural code. The general idea is that you create a custom class with all of the properties that are required to draw all of your lines. You would then declare a DataTemplate to define how the various properties are to be data bound, perhaps a little something like this:
<DataTemplate DataType="{x:Type YourXamlNamespacePrefix:GraphLine}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" />
</DataTemplate>
Then you create a collection of your custom class instances and data bind it to some collection control, like an ItemsControl and each one will be automatically rendered in the correct location:
<ItemsControl ItemsSource="{Binding YourGraphLineCollection, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:YourControlName}}}" />
Welcome to the powerful world of WPF data binding and DataTemplates.
UPDATE >>>
The custom class to data bind to the Line elements is not a view model. Think of it as a data type class, for which you will declare a DataTemplate like the one above. When I said that it should have all of the required properties, if you look at the above example, you'll see that it would at least need four double properties to data bind to the four used properties of the Line element. However, you might also choose to add further properties to data bind to the Stroke, StrokeThickness or Fill properties for example.
As for where you should define the DataTemplate, it should be within scope of the items that have it applied. If you want to use it in one view, then put it in the UserControl.Resources section of that view. However, if you want to use the same DataTemplate, then you should put it into the Application.Resources section of the App.xaml file because those Resources are available application wide.
FINAL UPDATE >>>
As noted in my comment, teaching users how to use WPF is definitely out of scope for this website, so I won't be doing that. To learn about DataTemplates, you should read the Data Templating Overview page on MSDN. When you don't know about something, MSDN should always be your first place to search for answers.
I can give you a few last tips before I go: The DependencyProperty in your control should be of type ObservableCollection<GraphLine>. Inside your control, you should data bind them to some sort of ItemsControl as shown above - I changed the Binding Path in it because you should really use a RelativeSource Binding to locate the property in your situation (where YourControlName is the name of your UserControl where you want to draw the Line objects).
Finally, in your view model (that is linked with the view that contains your new UserControl that draws the lines), you'll need a collection property to data bind with the collection in the UserControl, let's say named YourGraphLineCollectionInViewModel:
<YourXamlNamespacePrefix:YourControlName YourGraphLineCollection="{Binding
YourGraphLineCollectionInViewModel}" />
It's in this view model that you add the instances of your GraphLine class into the YourGraphLineCollectionInViewModel collection and as long as you have set up your Binding Paths as shown here, they'll appear in your UI within the ItemsControl. I am assuming that you know how to correctly set your DataContext - if not, you can easily find out how to do that online.
I have the following working XAML code:
<Window x:Class="DrawShape.Window1"
...
<Grid>
<Polygon Name="poly"/>
</Grid>
</Window>
In the corresponding C# code, a static callback method (for a property called Sides) accesses the poly element as follows:
static void OnSidesChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
Window1 win = obj as Window1;
win.poly.Points.Clear();
...
How is it that poly is accessed directly through Window1 win? poly is nested within a Grid element (albeit nameless). Is this type of access a feature of WPF?
PS: I am aware about the need for access through an object (because the method is static), it is the nesting that I don't understand.
You are confusing the WPF logical tree with how names are handled in XAML. In the logical tree the Polygon is contained in the Grid. However, all names belong to the same scope and are available as fields in the class generated from the XAML.
However, WPF has the concept of Namescopes which makes it possible to use the same name in multiple scopes.
Styles and templates in WPF provide the ability to reuse and reapply content in a straightforward way. However, styles and templates might also include elements with XAML names defined at the template level. That same template might be used multiple times in a page. For this reason, styles and templates both define their own XAML namescopes, independent of whatever location in an object tree where the style or template is applied.
In the simple XAML below you have a Grid named grid containing a ListBox named listBox. In the class generated from the XAML there are fields named grid and listBox allowing the code behind to access both controls.
Each list box item generated by the ItemTemplate contains a TextBlock named textBlock. However, each list box item is in a separate Namescope and there is no field named textBlock in the class generated from the XAML.
<Grid x:Name="grid">
<ListBox x:Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="textBlock" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
In this simple example there is no need to name the TextBlock objects. However, in more advanced scenarios you may want to refer to named elements within the template, e.g. in triggers.
Locate the file Window1.g.cs in your project directory.
Window1.g.cs contains a partial class that was generated from your XAML. In there you find the variable definition for poly.
I'm adding a close button to my tabs using the following guide:
http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem
This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. I'm binding the tab control using mvvm, so the parent property is apparently not being set and giving me a null reference exception for the parent when the event tries to remove from it.
Here's the binding so you get the idea:
<TabControl Name="tabControl" Margin="0,22,0.2,-5.2" ItemsSource="{Binding Tabs}" Background="#FF4C76B2"/>
Heres where the tabs are being added.
private void AddTab(object tabName)
{
ClosableTab newTab = new ClosableTab();
newTab.Title = "title?";
//newTab.Header = tabName;
TextBox test = new TextBox();
test.Text = "CONTENT (" + tabName + ") GOES HERE";
newTab.Content = test;
Tabs.Add(newTab);
OnPropertyChanged("Tabs");
}
Here is the event where the null reference is taking place:
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
As I see it there are two options:
try to find another way to remove the tab (without the parent
property)
try to find a way to somehow set the parent property (which cant be
done directly, it throws a compiler error)
That doesn't sound like MVVM to me. We work with data, not UI elements. We work with collections of classes that contain all of the properties required to fulfil some requirement and data bind those properties to the UI controls in DataTemplates. In this way, we add UI controls by adding data items into these collections and let the wonderful WPF templating system take care of the UI.
For example, you have a TabControl that we want to add or remove TabItems from... in a proper MVVM way. First, we need a collection of items that can represent each TabItem:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(TestView));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
I'm just using a DependencyProperty because I knocked this up in a UserControl and I'm just using a collection of strings for simplicity. You'll need to create a class that contains all of the data required for the whole TabItem content. Next, let's see the TabControl:
<TabControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" />
We data bind the collection to the TabControl.ItemsSource property and we set the TabControl.ItemTemplate to a Resource named ItemTemplate. Let's see that now:
xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type System:String}">
<TabItem Header="{Binding}" />
</DataTemplate>
This DataTemplate defines what each item in our collection will look like. For simplicity's sake, our strings are just data bound to the TabItem.Header property. This means that for each item we add into the collection, we'll now get a new TabItem with its Header property set to the value of the string:
Items.Add("Tab 1");
Items.Add("Tab 2");
Items.Add("Tab 3");
Note that I included the System XML Namespace Prefix for completeness, but you won't need that because your DataType will be your own custom class. You'll need more DataTemplates too. For example, if your custom class had a Header property and a Content property, which was another custom class, let's say called Content, that contained all of the properties for the TabItem.Content property, you could do this:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type YourPrefix:YourClass}">
<TabItem Header="{Binding Header}" Content="{Binding Content}" />
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:Content}">
<YourPrefix:SomeUserControl DataContext="{Binding}" />
</DataTemplate>
So this would give you TabItems with Headers set and Content that comes from SomeUserControl which you could design. You don't need to use UserControls, you could just add more UI controls to either DataTemplate. But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface.
And finally, to answer your question in the proper MVVM way... to remove a TabItem, you simply remove the item that relates to that TabItem from the collection. Simple... or it would have been if you really had been using MVVM like you claim. It's really worth learning MVVM properly as you'll soon see the benefits. I'll leave you to find your own tutorials as there are many to chose from.
UPDATE >>>
Your event handling is still not so MVVM... you don't need to pass a reference of any view model anywhere. The MVVM way is to use commands in the view model. In particular, you should investigate the RelayCommand. I have my own version, but these commands enable us to perform actions from data bound Buttons and other UI controls using methods or inline delegates in the view model (where action and canExecute in this example are the CommandParameter values):
<Button Content="Close Tab" Command="{Binding CloseTabCommand}"
CommandParameter="{Binding}" />
...
public ICommand CloseTabCommand
{
get { return new ActionCommand(action => Items.Remove(action),
canExecute => canExecute != null && Items.Contains(canExecute)); }
}
So whatever view model has your Tabs collection should have an AddTabCommand and a CloseTabCommand that add and remove items from the Tabs collection. But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. Use a DataTemplate to specify it if it is a UI control.
You can find out about the RelayCommand from this article on MSDN.
i have a datatemplate declared in xaml.
for e.g.
<DataTemplate x:Key="TestTemplate">
<StackPanel>
<TextBox Name="txtBox" Visibility="Visible"></TextBox>
</StackPanel>
</DataTemplate>
I wish to set the binding for txtBox in code behind before the element is generated because i have different binding paths for different elements that get generated
I can get the template in the code behind as :
DataTemplate tmplt = FindResource("TestTemplate") as DataTemplate;
but i am not sure what to do next. How to get the the txtBox reference to set the binding.
We have to remember one thing that Templates are not instantiated UI controls. They are streamed obejcts in XAML and are shared between UI elements. So if you edit a dataTemplate and change its stucture (by adding, editing, deleting an element under the template) it would change the one data template which is shared among controls. Thus other elements using that template will also be affected by the change.
Now lets address your issue of adding a dynamic biding to a textbox. You say each generated textbox will have different binding paths. So this definitely does NOT call for changing the data template itself!
You will have to access the text box and add dynamic bindings to it AFTER the textbox's is generated.
I see that your binding differs based on your "situation", so why cant you use TemplateSelector? Template selector will decide which data template (having one specific binding applied to the TetxBox) at runtime.
The first part of answer - is FindName() method.
example:
DataTemplate tmplt = FindResource("TestTemplate") as DataTemplate;
TextBox my = (TextBox)tmplt.FindName("txtBox");
try out this, it should help to get access to TextBox control. I think that you know how to bind to. If you want your DataBinding behave different way, use MultiBinding and Converter.
EDIT
public class GeneralObject
{
private object someObject;
public GeneralObject(object initObject)
{
this.someObject = initObject;
}
//If you want to bind to some text, for example
public string Text
{
get
{
//I think you know which objects are coming as input
if (this.someObject is SpecialClass1)
return ((SpecialClass1)this.someObject).SpecialClass1TextProperty;
if (this.someObject is SpecialClass2)
return ((SpecialClass2)this.someObject).SpecialClass2TextProperty;
//and so on.
}
}
}
EDIT 2
One more possible way
So I remember, that WPF have ContentControl!
<ContentControl Content="{Binding Path=CurrentObject}"/>
But in this case you have to create number of DataTemplate's, every Template for one class.
<DataTemplate DataType="{x:Type local:SpecialClass1}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:SpecialClass2}">
...
</DataTemplate>
<!--and so on-->
WPF resolve DataTypes of ContentControl.Content property, and put to the ContentControl right DataTemplate.
I have a ListBox in WPF where I set ItemsSource Property in Code to a List of "List"
When i now run the Program, i get a List with my classname with as much entrys as the List contains. Thats correct.
Now i secify the following Datatemplate:
<DataTemplate>
<NetworkEditor:NetworkEditor DisplayNetwork="{Binding}"></NetworkEditor:NetworkEditor>
</DataTemplate>
But to the DependencyPropery "DisplayNetwork" is always passed "null" (I tested this with a DebugValueConverter).
Any Ideas?
Xaml of the List Box:
<ListBox Name="myLst" Grid.Row="3" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<NetworkEditor:NetworkEditor DisplayNetwork="{Binding}"></NetworkEditor:NetworkEditor>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Implementaion of the Property in my UserControl:
public Network DisplayNetwork
{
get { return (Network)GetValue(DisplayNetworkProperty); }
set { SetValue(DisplayNetworkProperty, value); }
}
// Using a DependencyProperty as the backing store for DisplayNetwork. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayNetworkProperty =
DependencyProperty.Register("DisplayNetwork", typeof(Network), typeof(NetworkEditor), new FrameworkPropertyMetadata(null, OnDisplayNetworkChanged, CoerceValueCallback));
private static Object CoerceValueCallback(DependencyObject d,Object baseValue)
{
return baseValue;
}
OnDisplayNetworkChanged is never called, because null is always set as Value!
Data Source of my ListBox:
myLst.ItemsSource = ((S7FunctionBlock) myBlock).Networks;
where Networks is a List, and when I debug this Line, it contains data!
Have you checked your NetworkEditor:NetworkEditor class, if you set the DataContext there in. It is an often made mistake to set the DataContext from within a class and then to try accessing the DataContext from Xaml on this tag, thinking that the parents DataContext will be returned.
For checking this, try to change your XAML to the following.
<DataTemplate>
<Grid>
<NetworkEditor:NetworkEditor DisplayNetwork="{Path=DataContext,RelativeSource={RelativeSource,Mode=FindAncestor,AncestorType=Grid}}">
</NetworkEditor:NetworkEditor>
</Grid>
</DataTemplate>
Make a comment if there is an error in, I have not tested it. However, as also Will Dean says, in this case changing the NetworkEditor would be a good idea, if it is under your control.
As HCL says, the DataContext of that control is probably not what you think it is.
To diagnose this, you could change {Binding} to {Binding SomethingThatDoesntExist}, then turn on binding warnings in VS. The binding warning message will tell you which type of object was checked for 'SomethingThatDoesntExist' - you'll probably find it's not what you expected.
If it does turn out to be that NetworkEditor is setting its DataContext to something different to what you think, then a good solution to this problem (provided NetworkEditor is under your control), is to change to setting DataContext on the first object within NetworkEditor (often a Grid in a typical UserControl), rather than on the NetworkEditor object itself.