ā Creating A Share Button in avalonia
I have a button
This button is bound to a function in the MainWindowViewModel.cs file
When this button is clicked, I'm wanting it to take the selected item the user has chosen in the DataGrid and pretty print it to a microsoft word document file, open the users file explorer so that the user can select where they want to save the file to, and then create and save the file to that desired location. I have zero idea how to do this. Thanks in advance
73 Replies
I found the
Aspose.Words
library for creating a word doc file, but I haven't written in doc.Save(filename);
because I need to be able to open the file explorer and get access to where the user chose to save the file
https://stackoverflow.com/questions/1132422/open-a-folder-using-process-start
https://stackoverflow.com/questions/12822337/c-sharp-get-explorer-exe-to-return-a-file-path
I've also tried using these two stack overflows as reference for workign with the file dialog thing, but I'm not making any progress...To get a file dialog in avalonia you'd do
https://docs.avaloniaui.net/docs/next/basics/user-interface/file-dialogs
do I have to build an entire ViewModels file just for this or can I do this in my current function and just make the function asyncronous?
If you just want to make it work, yes you can do it in your current function
it's just a simple asking the user where they want to save the file and then creating the word doc and saving it to that location.
but if I need to make an entire usercontrol for it, then I can
You don't need to make a user control for it, in MVVM you might abstract it behind a service, but that abstraction is not really needed in this case
I don't know what "abstract it behind a service" means, but I didn't think it'd be a whole bunch of code to do it lol
Btw you should bind SelectedItem instead of getting it from the control itself
I dislike preference programming lol one dev will tell me to bind it to a variable and use that variable and another dev will tell me I don't need to do that and just call it directly....so confusing
Basically, you might structure your code something like
You then would simply have an instance of IFileService in your view model, and open the save dialog and get IStorageFile from it, the idea behind that is the view model doesn't really care about how it got the
IStorageFile
, so it's abstracted away from it (You don't have the implementation in the ViewModel itself)if this is more efficient, then I do not mind learning it this way
Binding is actually the right MVVM way, because your view model should not really have access to controls directly
Sure, so you might declare an interface
And then implement it like avalonia docs suggest (difference would returning an IStorageFile instead of directly opening it and writing to it)
ok so I created the two files that you suggested would be MVVM appropriate. IStorageFile is erroring because I don't know the import
unless it's not an import
that code and the picture are two different things to me
is the picture the
FileService.cs
part?
like I'd use the FileService.cs
file to create the text file with writing the information to it and such, and then use the other file to get the save location and save it?oops sorry I didn't clarify that
IFileService
is an interface not a class, btw place the cursor over IStorageFile
visual studio should suggest the namespace to add
The idea is that the view model doesn't really care about how it got the IStorageFile
instance, it simply needs gets one from an IFileService
instance and then writes to it
Note the capital I
in both IStorageFile
and IFileService
They are interfacesso I have to build an interface for it? like an axaml file?
interfaces are basically contracts, you can't instantiate an interface by itself, but a class/struct can implement them
You declare an interface like this
ok so I have the interface
what am I supposed to do with it? I do apologize for being dumb. Learning this for the first time and wanna go step by step
It's totally fine!
You now need to declare what methods/properties you expect the interface to have
In this case you'd need
public Task<IStorageFile> GetSaveFile();
(notice how the method doesn't have a body, it's simply a "contract"oh ok so no
{ }
needed for itso when putting that line in, it's not wanting a namespace, it's wanting to create another interface. Is that correct?
or do I have line 5 wrong?
instead of internal class it should be interface
Hmm, no, which version of avalonia are you using?
0.10.21
if I update the version, I have to go back through the entire project and change almost everything. I learned this version because the Todo App tutorial on avalonia is written off 0.10.21 and they haven't updated to 11 yet
I don't see anything on the docs site when changing it from 11 to 0.10.x for user interfaces or dialogs. Was it not a thing?
Well, 0.10.x had a different dialog api, which would make it harder to implement MVVM, because
SaveFileDialog.ShowAsync
takes in a window instance, so you'd have do something like this
I think you had a dependency on ReactiveUI?I am using ReactiveUI over not CommunityToolkit
how difficult is it going to make this project efficient? If I need to re-write it in version 11, I can work on that and then get back to you once I've done that but it'll take me a couple of days
and if I update to version 11, I'll be switching from ReactiveUI to CommunityToolkit so that I can use [ReactiveCommand] for my commands
which tbh I should probably do
You don't need to switch to version 11, basically you want a way to invoke a method on your MainWindow instance, so something like
(this is in MainWindow.axaml.cs)
so I no longer need the services files I created?
or do I still need them
and this is my current MainWindow.axaml.cs file. If I incorporate that, would it be like
?
or would it take place of the public MainWindow()?
like
No no, like what you did here
You can keep them if you want to refactor the code later
ok I'm going to remove them for now so I don't get confused
ok so here's what I've got
and that error is wanting me to do one of these
What this code does is register a handler for a ReactiveUI interaction, ReactiveUI interaction is a basically a callback that allows you to provide a handler that takes
input and provides output
Because you don't want to provide any input to the SaveFileDialoge, you can pass
Unit
In the viemodels constructror initialize itok I accidentally clicked
Introduce local for 'vm.ShowSaveFileDialog.RegisterHandler(ShowSaveFileDialog)
and although the error went away from ShowSaveFileDialog, I have no idea what it did and where it did it at. It didn't do anything to this file
found it
so in the MainWindowViewModel, I want to create
right?Remove
internal readonly object ShowSaveFileDialog;
And in the constructor do
ShowSaveFileDialog = new Interaction<Unit, string?>();
Which would initialize the property ShowSaveFileDialog to a new ReactiveUI Interactionand that's in the constructor of the MainWindowViewModel correct?
Yup!
ok so I'm missing some things
or does it belong there my bad
I know it goes in one of those two places......
Here, that would be the constructor!
ok cool. so now it's telling me that ShowSaveFileDialog doesn't exist in this instance
so do I need to pass it as a paremeter?
or should I generate a new field above the constructor
You need to readonly property for it
A readonly proeprty looks something like this
PropertyType PropertyName { get; }
ok so now we have this
but you said read only
so public readonly ...
You can't add
readonly
to properties
To make a property readonly you provide a getter
and no setter
ok I removed it
Okay so now to simply get a file from the showsafefiledialog
Interaction
provides a method Handle
which will call the handler you registered earlier
So
ShowSaveFileDialog.Handle(Unit.Default)
will call private async Task ShowSaveFileDialog(InteractionContext<Unit, string?> interaction)
(The one in the MainWindow)
Make sure to await Handle
You can call ShowSaveFileDialog.Handle(Unit.Default)
wherever you defined the saving procedure.
the share button in DiaryListVIew.axaml is bound to the ShareEntry() function in MainWindowVIewModel which is where we just did
they're now in the same file
the button is bound like this
Make sure to
await
Handle so ShareEntry should be public async Task ShareEntry()
so this will be an async function then
Yup, any function that awaits should be async
ok cool
so what's next?
because the thought of flow is
- Prompt user via file explorer on where to save the exported information
- save file location to a variable
- create the microsoft word document
- save the new document to the desired file location
Well, SaveEntry now will show the user a save file dialog
And the file location is the value returned from ShowSaveFileDialog.Handle
ok so I'm not sure if I was supposed to test the program to see if the file explorer window opened or not, but I did and when I clicked the share entry button, I got this
You didn't register a handler
Paste the code for MainWindow
Not MainWindowViewModel
MainWindow.axaml.cs
I've followed every step you gave me I thought
My bad, OnInitialized gets called before the DataContext is set to the ViewModel
I guess you could go full ReactiveUI then
ok so what do I need to do?
Change MainWindow declerate to this
public partial class MainWindow : ReactiveWindow<MainWindowViewMode>
What this would do is register the handler when the view gets activated
So the view model would be setshould this line say instead of this
public partial class MainWindow : ReactiveWindow<MainWindowViewMode>
it would be this public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
?MainWindowViewModel
š
Ignore the null ref warning for now
What does the error say?
It says the ReactiveUI<MainWindowViewModel> is less accessible than MainWindow
I'll have to go now, good luck, you're basically 90% done <:MadoThumbsUp_MM:406514447973351444>
90% done and have zero idea on how to fix this. Thanks for the help though
Accessibility = public / private / internal etc
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.