I have been searching everywhere for this. Just as the title says, is there a way for a XamlWriter to read the content of a text box, as well as the definition of the text box itself?
The text boxes are not directly entered into the Xaml, as they have been binded to the MVVM pattern, at runtime. At the moment, the XamlWriter simply saves the text boxes without their content.
Example code saving the view, but without text box content:
string mystrXAML = XamlWriter.Save(StackPanelContent);
string name = txtname.Text;
FileStream fs = File.Create(String.Format(#"C:\Desktop\" + name + ".txt"));
StreamWriter sw = new StreamWriter(fs);
sw.Write(mystrXAML);
sw.Close();
fs.Close();
EDIT:
Have spent alot of time thinking and researching how to do this still cant find the right answer. This is my XAML code and its still not saving the text box content and this is because they are binded (Is what i researched)
XAML:
<ListView ItemsSource="{Binding Title}" ItemTemplate="{DynamicResource Template}">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="20,20,0,0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="Template">
<TextBox Text="{Binding .}" Width="200" HorizontalAlignment="Left" />
</DataTemplate>
</ListView.Resources>
</ListView>
<ListView x:Name="QuestionList" ItemsSource="{Binding Question}" ItemTemplate {DynamicResource Template}">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="40,20,0,0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="Template">
<TextBox Text="{Binding .}" Width="200" HorizontalAlignment="Left"/>
</DataTemplate>
</ListView.Resources>
</ListView>
I just don't understand you... you come to this site and ask for helpful advice and when that helpful advice is given to you, you ignore it and ask your (practically same) question again and again. You've already been told by several different users that in WPF, we work with data, not UI controls. So rather than save the TextBoxes with all their extra properties that are meaningless to you, you should save the just text in the collections data bound to the TextBoxes.
Now I hope that you don't take this personally again, but at some point, you have to start listening to the people that know what they're talking about. However, if your heart is set on doing this in your own stubborn way, rather than the correct way, then may I draw your attention to the XamlWriter Class page on MSDN:
The serialization enabled by this method has a series of limitations. This is because the serialization enabled is explicitly run-time, and does not have access to possible design-time information in the original XAML (if any). For details, see Serialization Limitations of XamlWriter.Save.
And from the linked page on MSDN:
Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output. This potentially freezes any databound or resource obtained value to be the value last used by the run-time representation, with only limited or indirect ability to distinguish such a value from any other value set locally.
I've highlighted a section that basically says that yes, you can save the property values of the controls using the XamlWriter.Save method. However, I can't tell you why your values aren't being saved.
Now when you said that you had searched everywhere, you seem to have missed the most obvious search terms for your problem: XamlWriter Class. For future reference, had you searched with these words, then you'd have found all this out in the top result.
However, it just makes far more sense to save the data and not the UI controls. Think about it... you already have the mechanism to generate/display these TextBoxes from your collections, so why wouldn't you just use that? Save the data in a database, or a text file and when you reload it, use your existing code to re-populate the TextBoxes from the collections. It really is as simple as populating a collection in your view model with the loaded data. You could even save the data collection with almost the same code that you're using already, or less even.
That is how it is done properly. Now you can either accept that and move on with your project in the correct manner, or you can continue to ignore this advise and to ask more virtually identical questions. Right now, I think that this is at least your 3rd question asking how to store XAML in a file or database. You could have saved everyone including yourself a lot of time if you had just accepted the good advise that was given to you previously, or even done a proper search.
Apart from anything else, it's just so much easier persisting the data instead of the UI controls.
Finally, did you notice how your (very similar) question had not been answered until now? Have you seen how most questions are normally answered immediately here? I'm not the only person to notice all of your very similar questions and this shows you that the StackOverflow community as a whole do not like it when users abuse this website by expecting others to do all of their work for them.
Now you probably won't believe this as we've 'clashed' before, but all of this advice is meant to help you. Apologies if any of my comments have offended you, but if you use this site according to the rules, then you'll get much more out of it. For example, rather than asking the same question again and again, just edit your original question if you didn't get a satisfactory answer. An edited question gets posted back to the top of the unanswered queue again to attract more attention.
I hope you'll accept my advice this time and wish you luck with your project.
UPDATE >>>
The linked pages from MSDN show us that the XamlWriter can save data bound data, but unfortunately, I can't tell you why yours is not working.
To be honest, saving strings is not really enough. You need to save all of the relevant data. The simplest way to display, edit and save this data is to create custom classes that implement the INotifyPropertyChanged interface. For example, for you scenario, I would create a Question class something like this:
[Serializable]
public class Question // Implement INotifyPropertyChanged correctly here
{
public string Title { get; set; }
public string Text { get; set; }
public string Answer { get; set; }
}
If your questions are multiple choice, then you could do something like this:
[Serializable]
public class Question // Implement INotifyPropertyChanged correctly here
{
public string Title { get; set; }
public string Text { get; set; }
public List<Answer> Answers { get; set; }
public Answer CorrectAnswer { get; set; }
}
Of course, you should have private fields backing those public properties... this is just a simplified example. Note the use of the SerializableAttribute... this will enable you to save the data with just a few lines of code.
You can structure the properties however you want, but the idea is that you encapsulate all of the related data into one class. Then you can provide a DataTemplate for that class type, let's call it Template, and define a collection of controls that will display the relevant property values.
Now here's the good part... you can create a collection of that type in a view model...:
public ObservableCollection<Question> Questions { get; set; }
... and set the ItemTemplate property of any collection control to your DataTemplate:
<ListBox ItemsSource="{Binding Questions}" ItemTemplate="{StaticResource Template}" />
and then every Question item in the collection will magically be rendered exactly as you defined in the DataTemplate. So to answer your question about how do you reload the data, you just re-populate the Questions collection in the view model and the WPF templating system will correctly re-populate the UI for us in exactly the same way as it did before that data was saved.
Now as you can see... I've spent a great deal of time explaining this all to you. I trust that you can answer any further questions that you may have with your new found search skills.
To start you off, here are some useful articles:
Serialization (C# and Visual Basic)
Walkthrough: Persisting an Object (C# and Visual Basic)
Data Templating Overview
INotifyPropertyChanged Interface
Related
View:
<Picker ItemsSource="{Binding ECCLevels}"
ItemDisplayBinding="{Binding QRCodeGenerator.ECCLevel}"
HeightRequest="44"/>
ViewModel:
public ObservableCollection<QRCodeGenerator.ECCLevel> ECCLevels { get; set; } = new ObservableCollection<QRCodeGenerator.ECCLevel>();
ECCLevels = new ObservableCollection<QRCodeGenerator.ECCLevel>(Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).OfType<QRCodeGenerator.ECCLevel>().ToList());
I simplified the code above. The ObservableCollection gets filled with data after the second line of code. But the problem is I don't know what to put in the ItemDisplayBinding property of the picker since there the Enum gets converted into a list directly without going through a model.
You can just use ItemDisplayBinding="{Binding .}", the dot notation means that you refer to this basically. So, this way you simply reference the object itself and not any other property.
In the comments you asked me how I know, that is actually I great question. I think I picked it up as early as WPF. But it's hard to dig up from the documentation, I found a small notice of it here: https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.binding.path?redirectedfrom=MSDN&view=netframework-4.7.2#System_Windows_Data_Binding_Path
Optionally, a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}".
I'm sure there's a very logical explanation to the misbehavior (and I'm the most likely cause of that). Due to ignorance, I'm uncertain how to resolve it, though. Here's what transpires.
When I bound to a static resource as follow, the property Name automatically got displayed (it's begin served by the overridden ToString implementation).
<ComboBox ItemsSource="{x:Static local:MainWindow.AllOptions}"/>
Then, I try to rewrite the code to the MVVM pattern so I've changed that to this.
<ComboBox ItemsSource="{Binding AllOptions}"/>
Trying to set the Path explicitly doesn't work (nothing changes, the program executes well but shows nothing in the list. (A hint is that I've done something wrong because intellisense only suggests AllOptions and not Name as an alternative for Path.)
<ComboBox ItemsSource="{Binding AllOptions, Path=Name}"/>
So the switch to the view model causes the text of the property Name to disappear. I can see the number of elements served (and their contents) to be correct when breaking in the constructor for the viewmodel, which looks like this.
class Presenter
{
private IEnumerable<Stuff> _allOptions;
public IEnumerable<Stuff> AllOptions
=> _allOptions.Where(element => true);
public Presenter()
{
_allOptions = DataAccessor.GetStuff();
}
}
I've connected the viewmodel to the view like so.
<Window.DataContext>
<local:Presenter/>
</Window.DataContext>
What might I have missed?!
Edit
I just realized that the list bound to the component is empty. The binding doesn't occur. I was tricked by the outfolded combo box but when I empty the list, it seems to have a default number of lines it shows independently of the number of added elements! So, the problem is that my binding didn't manage to bind.
In a blog I see that the bloggers view model implements BindableBase. I have no such class according to the intellisense. Is INotify... a corresponding thingy?
Im building an application where I want to head for a design, that could remind of a dockpanel.
What I want, is having buttons in the left side (or left panel) representing different areas of the application (e.g "Milk", "Bread") and then have different "views" in the middle-panel.
What I already have tried, is making an application with a "Frontpage", and buttons changing the whole window/usercontrol - this however will not give me static areas/panels.
I do not want to use a tabcontrol with the tabtitemstrip being vertical - however it is kinda the same functionality im looking to have.
Any ideas?
Below is a picture with the wished design, to kinda give an idea of my thoughts.. Any help appreciated :)
http://s57.photobucket.com/user/RolleKn/media/wpfdesign_zps3737b014.jpg.html
If you use WPF, use ContainerControl or ContentPresenter for that.
In general, "switching Visibility On/Off" is not a good way to go. It forces the UI to create all objects, even those invisible ones, and to handle their data and events, etc.
And you need to switch it all manually.
WPF provides you with many mechanisms that can save you this. Some are smarter than others, some not.
One of the most basic mechanism in WPF is the Control and its Template property. You can replace whole your Grid+Contents+SwitchingVisibility idea with a single Control and switching its Template:
<Window.Resources>
<ControlTemplate x:Key="panel1"> ..carrots.. </ControlTemplate>
<ControlTemplate x:Key="panel2"> ..cucubers.. </ControlTemplate>
<ControlTemplate x:Key="panel3"> ..donkey.. </ControlTemplate>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Control x:Name="foo" />
</Grid>
Now, if you get the foo and set its .Template and set it to panel1, then the "carrots" will show up. if you set it to panel3, donkeys. And so on.
It's very powerful, but it will not be really handy due to some other things I won't cover. There are books and tutorials that explain Templates in depth. Also, this mechanism is really not designed for such task. It's the most basic one, and a good thing to know if you want to work in WPF, but there are more suitable ones here.
Second next powerful and still basic mechanism is ContentControl/ContentPresenter. They work almost in the same way (actually CC uses CP internally), so I'll skip it.
ContentControl is a smart control that knows how to automatically select a correct Template with respect to the data you are tryng to present.
So:
<Window.Resources>
<DataTemplate DataType="CarrotData"> ..carrots.. </..>
<DataTemplate DataType="CucumberData"> ..cucubers.. </..>
<DataTemplate DataType="DonkeyData"> ..donkey.. </..>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ContentControl x:Name="foo" Content="{Binding ..}" />
</Grid>
Note the change from 'ControlTemplate' to 'DataTemplate'.
Now, with this setting, you don't even need to switch templates manually. You just get the "foo" and set its Content to either:
a CarrotData object, that contains the carrot-related data
a CucumberData object, that contains the cucumber-related data
a DonkeyData object, that contains the donkey-related data
Once you set the data to be shown (i.e. foo.Content = carrots[5]), the ContentControl will pick the relevant template to be shown.
You can bind the Content property to just about anything. If you have some dataclass that contains carrots/donkeys and has a property CurrentThing, you can bind to it and ContentControll will switch the views automatically along with the changes to CurrentThing.
That's basics. There's much more to it, in almost any point I tried to briefly cover. For now, leave ControlTemplates. Read about DataTemplates and Bindings. Read about ContentPresenter (shows 1 template for 1 item) and ItemsControl (shows N items+templates). Then, read a little on MVVM pattern.
You will quickly see that "having everything in one Grid" and "switching Visibility" is an odd way to do it.
However, I wouldn't be fair if I didn't mention that everything has a cost included. Extensive use of templates and bindings makes your app a bit slower compared to what you could get when you do everything manually. But usually, doing it manually is just not really worth it.
I am currently customizing a ListBox. With that I mean adding an image-element, second line-element etc. to one listItem.
The itemSource is a List in C#, therefore I have no preview of the items in Expression Blend/VS. And that's the pain.
Because I have always to edit the XAML and then deploy to check. And this goes on and on until the last pixel is correct.
Isn't there a way, of editing a ListBox with custom items (with a dynamic itemSource) live in Blend/VS?
That would really fasten up my developing.
If you want to see how your controls look like in design time, you must use SampleData. There are several ways to do it, it depends on your framework.
Let's say you have a page named MainPage.xaml. If you don't have view model already, create a new one and name it MainViewModel.cs. Define all public properties that will be used for binding.
Once you have your view model, create new file in a folder named SampleData and name it MainViewModelSampleData.xaml.
Now, in the MainPage.xaml add the following attribute to the page element:
d:DataContext={d:DesignData Source=SampleData/MainViewModelSampleData.xaml}
Also set Build Action for MainViewModelSampleData.xaml to DesignData.
Now, if you want to display data in your MainPage, you need to define all properties in the sample data file. For example:
// view model contains public properties Title of type string and Children of type
// PersonViewModel which contains properties Name and Age (string and int respectively)
<local:MainViewModel xmlns:local="clr-namespace:myapp"
Title="Title">
<local:MainViewModel.Children>
<local:ChildViewModel Name="John" Age="31" />
</local:MainViewModel.Children>
</local:MainViewModel>
You should now see your page filled with data in your design view. This way by using MVVM you can create mock data quickly. That will ensure that you can design your view around existing data without running the application.
Read more on the following links:
31 Days of Mango | Day #18: Using Sample Data
Modify sample data
Generate sample data
Using Blend Sample data at Design time and real data at Runtime
I now know how to do that.
If anyone if you guys ever stumble upon this problem, do this:
Copy all the XAML you wrote in the stackpanel of your itemtemplate
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
//...
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
And copy it up to <ListBox> //Here </ListBox>
There you can edit it in the designer.
And when you're done, just copy the code back to the StackPanel.
I've been tying different things / reading up on this issue for awhile now and have not yet found an answer. Hopefully you guys can help.
I have an observablecollection of type string. I want to bind this collection to a datagrid and be able to edit/delete/add to the collection. Here is my xaml:
<DataGrid ItemsSource="{Binding Movies.Titles}" CanUserDeleteRows="True" CanUserAddRows="True" Height="300">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"/>
</DataGrid.Columns>
</DataGrid>
The same observablecollection is also bound to a listbox. I want to be able to edit the collection using the datagrid method (above) and see the changes/edits in the listbox. The delete/add is working correctly, but when I edit a string inside a grid cell and it loses focus, the string goes back to what it originally was and never gets updated.
Thanks a lot for any help / suggestions.
Wow, I went to do this yesterday and was stuck with a DataGrid that would add a new line for my ObservableCollection. After research, I realized why. Strings and immutable.
I found this question and unfortunately it didn't have an answer. So I can't leave this empty of an answer.
So here are the answers I found:
DataGrid cannot update a string collection by adding, editing, or removing strings.
I found a workaround to wrap the string in a StringWrapper object. Here it is.
public class StringWrapper
{
public string Text { get; set; }
}
I didn't like either answer.
The original question asker, moncadad, looks like he wants a one column DataGrid. He probably just wants to add and remove strings from an ObservableCollection without a lot of code. Edit probably isn't too important since that can be done by delete and add again.
I ended up doing this myself with a reusable usercontrol I made called StringListBox.
A ListBox for strings that supports add and delete
Basically the idea is to create the look of a DataGrid with a Label, a ListBox, a TextBox and an Add button and since this is a control, it has to work with ObservableObject or List in one control.
This give you Add and Delete. I doesn't provide edit.
Hope this helps the next guy.
Actually it works, you should just use
Mode=OneWay
in your binding.
I hope this helps!