WPF How to create, save and load multiple setting files - wpf

In my WPF application I have settings accessed by Properties.Settings.Default.Something.
In these user settings I'm saving different textbox, radiobutton, checkbox values.
I need to have sets of these settings depending on one combobox choice, and saved it. For example, user picks "1" in combobox, sets text in textbox, picks 2, sets text in textbox again. After reopening application I want those textbox values saved. Content of combobox options is generated dynamically.
I know those settings are saved in config file located in Users/appdata/... but I have no clue how and if its even possible to make multiple files like this to be manually saved and loaded on runtime.

Serialize them as xml-file. Here is an generic example how to do this.
Please check out DataContract here
C#
private static T ReadXmlFile<T>(string path) where T : class
{
T result = null;
if (File.Exists(path))
{
try
{
using (XmlReader reader = XmlReader.Create(path))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
result = (T)serializer.ReadObject(reader);
}
}
catch (Exception ex)
{
throw ex; // or what ever
}
}
return result;
}
private static void WriteXmlFile<T>(string path, T content2write) where T : class
{
if (!Directory.Exists(Path.GetDirectoryName(path)))
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
}
using (XmlWriter writer = XmlWriter.Create(path,
new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
Encoding = Encoding.UTF8,
CloseOutput = true
}))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(writer, content2write);
}
}
Maybe save them in your own AppData-folder with Environment.SpecialFolder.LocalApplicationData ;-) and go like this
private static readonly string MyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), #"MyApp\AppDescription");

Related

Codename One back command update form data

I have an Android application that uses Back Command to go back to the start screen.
The start screen has a label with a number inside, that I want to update when the back command is used.
I could figure out a solution with the code inside the back command, but I don't know if my approach is the best, since the ClassOne gets sort of loaded twice.
Here is the code I already have:
public class ClassOne {
public ClassOne(ClassPojo classPojo) {
// I want to change the text of this label when calling the back command
labelOne.setText(classPojo.getStringTest());
formOne.show();
}
}
public class ClassTwo {
public ClassTwo(Form a , ClassPojo classPojo) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
// I am adding the new value for the label here inside the back command
classPojo.setStringTest("testing");
a.showBack();
new ClassOne(classPojo);
}
};
formTwo.setBackCommand(back);
}
I'm not sure what the problem is, your example is a bit generic. However, a complete minimal example where the startScreen form instance is not recreated is this one:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
button1.addActionListener(l -> {
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
form2.show();
});
If you don't even want to recreate the form2 instance, then you can do so:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
button1.addActionListener(l -> {
form2.show();
});
In my opinion, whether or not to recreate the instances of a Form should be evaluated on a case-by-case basis. Among the variables between taking into consideration, according to my modest opinion, there is also the readability of the code and what it does, especially in complex cases.
The overhead of recreating a form instance is negligible so that wouldn't be a problem but in recent years we try to reuse form instances more. Not because of the performance.
The benefit is in minor behaviors e.g. scroll position within the form. These are very hard to replicate.
During testing, I found an easy solution that is adding the label to the constructor. I hope this snippet can be helpful.
public ClassTwo(Form a, ClassPojo classPojo, Label label) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
label.setText(classPojo.getStringTest());
a.showBack();
}
};

How to highlight the word(s) with bold in text block for specified keyword using the attached property

The objective is to bold the word(s) of text in Textblock with matching input keyword.
For example: Stackoverflow is a very helpful, keep using Stackoverflow to sharpen your skills.
When the keyword is: Stackoverflow it should now display as
Stackoverflow is a very helpful, keep using Stackoverflow to sharpen your skills.
I tried to achieve the same objective using attached property. Below is the snap code of the same
public class HighLightKeyWord : DependencyObject
{
//This word is used to specify the word to highlight
public static readonly DependencyProperty BoldWordProperty = DependencyProperty.RegisterAttached("BoldWord", typeof(string), typeof(HighLightKeyWord),
new PropertyMetadata(string.Empty, OnBindingTextChanged));
public static string GetBoldWord(DependencyObject obj)
{
return (string)obj.GetValue(BoldWordProperty);
}
public static void SetBoldWord(DependencyObject obj, string value)
{
obj.SetValue(BoldWordProperty, value);
}
private static void OnBindingTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var _Key = e.NewValue as string;
var textblk = d as TextBlock;
string SourceText = textblk.Text;
SetBold(_Key, textblk, SourceText);
}
private static void SetBold(string key, TextBlock text, string SourceText)
{
text.Inlines.Clear();
var words = SourceText.Split(' ');
for (int i = 0; i < words.Length; i++)
{
var word = words[i];
var inline = new Run() { Text = word + ' ' };
if (String.Compare(word, key, StringComparison.CurrentCultureIgnoreCase) == 0)
{
inline.FontWeight = FontWeights.Bold;
}
text.Inlines.Add(inline);
}
}
}
// Bind object in Main
StackOverFlow stkovrflw = new StackOverFlow();
stkovrflw.Text = "Stackoverflow is a very helpful,keep using Stackoverflow to sharpen your skills.";
stkovrflw.KeyWord = "Stackoverflow";
this.DataContext = stkovrflw;
In Xaml I binded the value as
<TextBlock Text="{Binding Path=Text}" loc:HighLightKeyWord.BoldWord="{Binding Path=KeyWord}" />
Above code is working fine, however when I directly sets the HighlightText property in the xaml instead through data binding, Text property of the Text block is getting empty in the OnBindingTextChanged method and this method is called only once when dependency property is set .
I used this design based on Spellchecker concept so that other teamates can reuse my attached property in their projects.
Can anyone suggest how to fix the problem?
Whether you want to modify a TextBlock FontWeight attribute or another display property, I've written the following static method and found it very useful. (Note: The method illustrates highlighting text by modifying the Foreground property. The same principle can be used for any other TextBlock display attribute.)
static Brush DefaultHighlight = new SolidColorBrush(Colors.Red);
public static void SetTextAndHighlightSubstring(this TextBlock targetTextBlock, string sourceText, string subString, Brush highlight = null)
{
if (targetTextBlock == null || String.IsNullOrEmpty(sourceText) || String.IsNullOrEmpty(subString))
return;
targetTextBlock.Text = "";
var subStringPosition = sourceText.ToUpper().IndexOf(subString);
if (subStringPosition == -1)
{
targetTextBlock.Inlines.Add(new Run { Text = sourceText });
return;
}
var subStringLength = subString.Length;
var header = sourceText.Substring(0, subStringPosition);
subString = sourceText.Substring(subStringPosition, subStringLength);
var trailerLength = sourceText.Length - (subStringPosition + subStringLength);
var trailer = sourceText.Substring(subStringPosition + subStringLength, trailerLength);
targetTextBlock.Inlines.Add(new Run { Text = header });
targetTextBlock.Inlines.Add(new Run { Text = subString, Foreground = highlight ?? DefaultHighlight });
targetTextBlock.Inlines.Add(new Run { Text = trailer });
}
You can call this method using the TextBlock extension syntax like so:
TextBlockTitle.SetTextAndHighlightSubstring(_categoryItem.ItemText, _searchText);
In this example, the following display resulted:
I recently ran into the same problem with the broken text binding. I realize this is an old question, but figured I'd share my finding anyway.
Basically Text property's data-binding is severed the moment Inlines.Clear is called. The way I got around this problem was by adding an internal text dependency property that replicated text's binding so subsequent text changes would continue to trigger highlighting.
Try adding something like this before clearing text.Inlines.
protected static string GetInternalText(DependencyObject obj)
{
return (string)obj.GetValue(InternalTextProperty)
}
protected static void SetInternalText(DependencyObject obj, string value)
{
obj.SetValue(InternalTextProperty, value);
}
// Using a DependencyProperty as the backing store for InternalText. This enables animation, styling, binding, etc...
protected static readonly DependencyProperty InternalTextProperty =
DependencyProperty.RegisterAttached("InternalText", typeof(string),
typeof(HighlightableTextBlock), new PropertyMetadata(string.Empty, OnInternalTextChanged));
private static void SetBold(string key, TextBlock text, string SourceText)
{
//Add the following code to replicate text binding
if (textblock.GetBindingExpression(HighlightableTextBlock.InternalTextProperty) == null)
{
var textBinding = text.GetBindingExpression(TextBlock.TextProperty);
if (textBinding != null)
{
text.SetBinding(HighLightKeyWord.InternalTextProperty, textBinding.ParentBindingBase);
}
}
...
}
In InternalTextProperty's propertyChangeCallback you can have the same code as OnBindingTextChanged() or you could refactor them into another method.
For those who don't want to write their own code. I have created a lightweight nuget package that adds highlighting to regular TextBlock. You can leave most of your code intact and only have to add a few attached properties. By default the add-on supports highlighting, but you can also enable bold, italic and underline on the keyword.
https://www.nuget.org/packages/HighlightableTextBlock/
The source is here: https://github.com/kthsu/HighlightableTextBlock

Restrict text input inside combobox

I need to restrict text input inside combobox with positive numbers. I've searched stackoverflow for this and found similar question: Recommended way to restrict input in JavaFX textfield
The only difference is that the mentioned question addresses bare textfield. The answer approved by the javafx designers is to extend the TextField class and override couple of methods: replaceText and replaceSelection. This hack does not work with combobox: TextField instance if stored inside and is avaiable as read-only property named editor.
So what is the recommended way to restrict text input inside javafx combobox?
Since this question question never got a proper answer I'm adding a solution I've implemented that restricts the user to input that matches a regular expression and is shorter than a particular length (this is optional). This is done by adding a ChangeListener to the editor TextField. Any input that doesn't match will not get written into the editor TextField.
This example restricts the user to a maximum of two numeric characters.
ComboBox<Integer> integerComboBox = new ComboBox<Integer>();
integerComboBox.setEditable(true);
integerComboBox.getEditor().textProperty()
.addListener(new ChangeListener<String>() {
// The max length of the input
int maxLength = 2;
// The regular expression controlling the input, in this case we only allow number 0 to 9.
String restriction = "[0-9]";
private boolean ignore;
#Override
public void changed(
ObservableValue<? extends String> observableValue,
String oldValue, String newValue) {
if (ignore || newValue == null) {
return;
}
if (newValue.length() > maxLength) {
ignore = true;
integerComboBox.getEditor().setText(
newValue.substring(0, maxLength));
ignore = false;
}
if (!newValue.matches(restriction + "*")) {
ignore = true;
integerComboBox.getEditor().setText(oldValue);
ignore = false;
}
}
});
You might register a check method on the editor property to check if any input is accpetable.
Here I allow editing, but draw a red frame if values are not in the items list.
ObservableList<String> myChoices = FXCollections.observableArrayList();
void testComboBoxCheck(VBox box) {
myChoices.add("A");
myChoices.add("B");
myChoices.add("C");
ComboBox<String> first = new ComboBox<String>();
first.setItems(myChoices);
first.setEditable(true);
first.editorProperty().getValue().textProperty().addListener((v, o, n) -> {
if (myChoices.contains(n.toUpperCase())) {
first.setBackground(new Background(new BackgroundFill(Color.rgb(30,30,30), new CornerRadii(0), new Insets(0))));
} else {
first.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(0), new Insets(0))));
}
});
box.getChildren().addAll(first);
}
How about decouplign the Text editor from the ComboBox and link their values?
HBox combo = new HBox();
TextField editor = new TextField();
ComboBox<String> first = new ComboBox<String>();
first.setItems(myChoices);
first.setButtonCell(new ComboBoxListCell<String>(){
#Override public void updateItem(String s, boolean empty) {
super.updateItem(s, empty);
setText(null);
}
});
editor.textProperty().bindBidirectional(first.valueProperty());
combo.getChildren().addAll(editor, first);
box.getChildren().addAll(combo);
Now you have full conroll over the TextField allowing to override any methods etc.

Download a file through the WebBrowser control

I have a WebBrowser control on a form, but for the most part it remains hidden from the user. It is there to handle a series of login and other tasks. I have to use this control because there is a ton of Javascript that handles the login. (i.e., I can't just switch to a WebClient object.)
After hopping around a bit, we end up wanting to download a PDF file. But instead of downloading, the file is displayed within the webBrowser control, which the user can not see.
How can I download the PDF instead of having it load in the browser control?
Add a SaveFileDialog control to your form, then add the following code on your WebBrowser's Navigating event:
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
if (e.Url.Segments[e.Url.Segments.Length - 1].EndsWith(".pdf"))
{
e.Cancel = true;
string filepath = null;
saveFileDialog1.FileName = e.Url.Segments[e.Url.Segments.Length - 1];
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
filepath = saveFileDialog1.FileName;
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileAsync(e.Url, filepath);
}
}
}
//Callback function
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("File downloaded");
}
Source: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d338a2c8-96df-4cb0-b8be-c5fbdd7c9202
The solution I ended up using:
I did everything else as-needed to get the URL where it needed to go. Knowing that all of the login information, required settings, viewstates, etc. were stored in the cookies, I was finally able to grab the file using a hybrid of the web control to navigate then the WebClient object to actually snag the file bytes.
public byte[] GetPDF(string keyValue)
{
DoLogin();
// Ask the source to generate the PDF. The PDF doesn't
// exist on the server until you have visited this page
// at least ONCE. The PDF exists for five minutes after
// the visit, so you have to snag it pretty quick.
LoadUrl(string.Format(
"https://www.theMagicSource.com/getimage.do?&key={0}&imageoutputformat=PDF",
keyValue));
// Now that we're logged in (not shown here), and
// (hopefully) at the right location, snag the cookies.
// We can use them to download the PDF directly.
string cookies = GetCookies();
byte[] fileBytes = null;
try
{
// We are fully logged in, and by now, the PDF should
// be generated. GO GET IT!
WebClient wc = new WebClient();
wc.Headers.Add("Cookie: " + cookies);
string tmpFile = Path.GetTempFileName();
wc.DownloadFile(string.Format(
"https://www.theMagicSource.com/document?id={0}_final.PDF",
keyValue), tmpFile);
fileBytes = File.ReadAllBytes(tmpFile);
File.Delete(tmpFile);
}
catch (Exception ex)
{
// If we can't get the PDF here, then just ignore the error and return null.
throw new WebScrapePDFException(
"Could not find the specified file.", ex);
}
return fileBytes;
}
private void LoadUrl(string url)
{
InternalBrowser.Navigate(url);
// Let the browser control do what it needs to do to start
// processing the page.
Thread.Sleep(100);
// If EITHER we can't continue OR
// the web browser has not been idle for 10 consecutive seconds yet,
// then wait some more.
// ...
// ... Some stuff here to make sure the page is fully loaded and ready.
// ... Removed to reduce complexity, but you get the idea.
// ...
}
private string GetCookies()
{
if (InternalBrowser.InvokeRequired)
{
return (string)InternalBrowser.Invoke(new Func<string>(() => GetCookies()));
}
else
{
return InternalBrowser.Document.Cookie;
}
}
bool documentCompleted = false;
string getInnerText(string url)
{
documentCompleted = false;
web.Navigate(url);
while (!documentCompleted)
Application.DoEvents();
return web.Document.Body.InnerText;
}
private void web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
documentCompleted = true;
}

Best way of designing the flow of code when reading/writing from database

So I'm trying out a concept tool of mine where I need to be able to read and write data from a database real easy. I've set up the form as I like and spread around different text boxes and dropdownboxes to read the data from the database. And I've got it all to work and all, but there's a small bug I don't fully understand why's there. Some textboxes don't update the text from the database. But it seems as it only occurs if the data in the database is nothing. So the value from the last row is still hanging in the textbox and thus, clicking "Update" actually updates the value from the field from the last row, into the new row. Messing everything up.
Now, what I'm the most interested in is the shear flow of the code. What's the best way of laying out the code to do all this? So far I've got this:
This is the code when clicking on a cell in the datagridview:
Private Sub DataGridView_CellClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView.CellClick
On Error Resume Next
selectedName = Me.DataGridView.CurrentRow.Cells(0).Value
selectedGenre = Me.DataGridView.CurrentRow.Cells(1).Value
selectedRhytm = Me.DataGridView.CurrentRow.Cells(2).Value
selectedLength = Me.DataGridView.CurrentRow.Cells(3).Value
selectedFinished = Me.DataGridView.CurrentRow.Cells(4).Value
selectedSoundFile = Me.DataGridView.CurrentRow.Cells(5).Value
txtBoxName.Text = selectedName
txtBoxGenre.Text = selectedGenre
txtBoxRhytm.Text = selectedRhytm
txtBoxLength.Text = selectedLength
txtBoxFinished.Text = selectedFinished
txtBoxSoundFile.Text = selectedSoundFile
End Sub
The "selected"-variables are all declared in a GlobalCode.vb I've got where I create all of them for use later. They are defined like this:
Friend Module GlobalVariables
Friend selectedName As String = Nothing
Friend selectedGenre As String = Nothing
Friend selectedRhytm As String = Nothing
Friend selectedLength As String = Nothing
Friend selectedFinished As String = Nothing
Friend selectedSoundFile As String = Nothing
End Module
I haven't really done anything like this before. I'm more of a designer rather than programmer, but I really need to try out a concept so I'm not sure this is the way of doing this at all. I've found that it works, most of the times. But I reckon skilled programmers have a way of designing the layout of the code so it's efficient, clean and easy to read.
So how does this look?
(I can't see anything database related in the question, btw)
Perhaps the best way of laying out this code is... not to. Don't write code for things that the standard data-binding frameworks can handle. For example (sorry it is C#, but it should translate - all of the "good" bits here are provided by the .NET framework, not the language); some UI code - note no code to copy values:
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
// some sample data
BindingList<Track> tracks = new BindingList<Track>();
tracks.Add(new Track { Name = "foo", Genre = "Rock", Rhythm = "insane", Length = 180 });
tracks.Add(new Track { Name = "bar", Genre = "Classic", Rhythm = "sedate", Length = 240 });
// show the data on a form
using (Form form = new Form {
Controls = {
new DataGridView { DataSource = tracks, Dock = DockStyle.Fill },
new TextBox { DataBindings = {{"Text", tracks, "Name"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Genre"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Rhythm"}}, Dock = DockStyle.Bottom},
new TextBox { DataBindings = {{"Text", tracks, "Length"}}, Dock = DockStyle.Bottom},
}
}) {
Application.Run(form);
}
}
}
With supporting data entity:
class Track : INotifyPropertyChanged {
private string name, genre, rhythm;
private int length;
public event PropertyChangedEventHandler PropertyChanged;
private void SetField<T>(ref T field, T value, string propertyName) {
if (!EqualityComparer<T>.Default.Equals(field, value)) {
field = value;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Name { get { return name; } set { SetField(ref name, value, "Name"); } }
public string Genre { get { return genre; } set { SetField(ref genre, value, "Genre"); } }
public string Rhythm { get { return rhythm; } set { SetField(ref rhythm, value, "Rhythm"); } }
public int Length { get { return length; } set { SetField(ref length, value, "Length"); } }
}
Try commenting out On Error Resume Next for a bit and seeing what happens. I've managed to confuse myself more times than I can count with that statement.
EDIT
I just realized this is VB.Net. You shouldn't ever be using On Error Resume Next in this case. Use Try Catch structures.

Resources