├── .gitignore
├── LICENSE
├── README.md
├── doc
├── 0-Introduction
│ ├── README.md
│ ├── img
│ │ ├── 0601_01_Architecture.png
│ │ ├── 0601_02_AllPlatforms.png
│ │ ├── 0601_03_CompileAotIos.png
│ │ ├── 0601_04_CompileAndroid.png
│ │ └── solution.png
│ └── src
│ │ ├── MyNewsReader.sln
│ │ └── MyNewsReader
│ │ ├── App.xaml
│ │ ├── App.xaml.cs
│ │ ├── AppShell.xaml
│ │ ├── AppShell.xaml.cs
│ │ ├── Constants.cs
│ │ ├── MauiProgram.cs
│ │ ├── Models
│ │ ├── NewsFeed.cs
│ │ ├── Rss.cs
│ │ └── Rss_Serializer.cs
│ │ ├── MyNewsReader.csproj
│ │ ├── Platforms
│ │ ├── Android
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── MainActivity.cs
│ │ │ ├── MainApplication.cs
│ │ │ └── Resources
│ │ │ │ └── values
│ │ │ │ └── colors.xml
│ │ ├── MacCatalyst
│ │ │ ├── AppDelegate.cs
│ │ │ ├── Info.plist
│ │ │ └── Program.cs
│ │ ├── Tizen
│ │ │ ├── Main.cs
│ │ │ └── tizen-manifest.xml
│ │ ├── Windows
│ │ │ ├── App.xaml
│ │ │ ├── App.xaml.cs
│ │ │ ├── Package.appxmanifest
│ │ │ └── app.manifest
│ │ └── iOS
│ │ │ ├── AppDelegate.cs
│ │ │ ├── Info.plist
│ │ │ └── Program.cs
│ │ ├── Properties
│ │ └── launchSettings.json
│ │ ├── Resources
│ │ ├── AppIcon
│ │ │ ├── appicon.svg
│ │ │ └── appiconfg.svg
│ │ ├── Fonts
│ │ │ ├── OpenSans-Regular.ttf
│ │ │ └── OpenSans-Semibold.ttf
│ │ ├── Images
│ │ │ └── dotnet_bot.svg
│ │ ├── Raw
│ │ │ └── AboutAssets.txt
│ │ ├── Splash
│ │ │ └── splash.svg
│ │ └── Styles
│ │ │ ├── Colors.xaml
│ │ │ └── Styles.xaml
│ │ ├── Services
│ │ └── NewsFeedService.cs
│ │ ├── ViewModels
│ │ ├── AddNewsFeedViewModel.cs
│ │ ├── ArticleViewModel.cs
│ │ ├── BaseViewModel.cs
│ │ ├── NewsFeedListViewModel.cs
│ │ └── NewsFeedViewModel.cs
│ │ └── Views
│ │ ├── AddNewsFeed.xaml
│ │ ├── AddNewsFeed.xaml.cs
│ │ ├── ArticleContent.xaml
│ │ ├── ArticleContent.xaml.cs
│ │ ├── FeedContent.xaml
│ │ ├── FeedContent.xaml.cs
│ │ ├── MainPage.xaml
│ │ └── MainPage.xaml.cs
├── 1-BindingData
│ ├── README.md
│ ├── img
│ │ ├── layouts.png
│ │ └── results.png
│ └── src
│ │ ├── MyNewsReader.sln
│ │ └── MyNewsReader
│ │ ├── App.xaml
│ │ ├── App.xaml.cs
│ │ ├── AppShell.xaml
│ │ ├── AppShell.xaml.cs
│ │ ├── Constants.cs
│ │ ├── MauiProgram.cs
│ │ ├── Models
│ │ ├── NewsFeed.cs
│ │ ├── Rss.cs
│ │ └── Rss_Serializer.cs
│ │ ├── MyNewsReader.csproj
│ │ ├── Platforms
│ │ ├── Android
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── MainActivity.cs
│ │ │ ├── MainApplication.cs
│ │ │ └── Resources
│ │ │ │ └── values
│ │ │ │ └── colors.xml
│ │ ├── MacCatalyst
│ │ │ ├── AppDelegate.cs
│ │ │ ├── Info.plist
│ │ │ └── Program.cs
│ │ ├── Tizen
│ │ │ ├── Main.cs
│ │ │ └── tizen-manifest.xml
│ │ ├── Windows
│ │ │ ├── App.xaml
│ │ │ ├── App.xaml.cs
│ │ │ ├── Package.appxmanifest
│ │ │ └── app.manifest
│ │ └── iOS
│ │ │ ├── AppDelegate.cs
│ │ │ ├── Info.plist
│ │ │ └── Program.cs
│ │ ├── Properties
│ │ └── launchSettings.json
│ │ ├── Resources
│ │ ├── AppIcon
│ │ │ ├── appicon.svg
│ │ │ └── appiconfg.svg
│ │ ├── Fonts
│ │ │ ├── OpenSans-Regular.ttf
│ │ │ └── OpenSans-Semibold.ttf
│ │ ├── Images
│ │ │ └── dotnet_bot.svg
│ │ ├── Raw
│ │ │ └── AboutAssets.txt
│ │ ├── Splash
│ │ │ └── splash.svg
│ │ └── Styles
│ │ │ ├── Colors.xaml
│ │ │ └── Styles.xaml
│ │ ├── Services
│ │ └── NewsFeedService.cs
│ │ ├── ViewModels
│ │ ├── AddNewsFeedViewModel.cs
│ │ ├── ArticleViewModel.cs
│ │ ├── BaseViewModel.cs
│ │ ├── NewsFeedListViewModel.cs
│ │ └── NewsFeedViewModel.cs
│ │ └── Views
│ │ ├── AddNewsFeed.xaml
│ │ ├── AddNewsFeed.xaml.cs
│ │ ├── ArticleContent.xaml
│ │ ├── ArticleContent.xaml.cs
│ │ ├── FeedContent.xaml
│ │ ├── FeedContent.xaml.cs
│ │ ├── MainPage.xaml
│ │ └── MainPage.xaml.cs
├── 2-Data
│ └── README.md
├── 4-TheShell
│ └── README.md
└── img
│ ├── devexpress-light.png
│ ├── gap-mobilize-logo-lightbg.png
│ ├── iron-software-light.png
│ ├── progress-telerik-light.png
│ └── syncfusion-light.png
└── src
├── MyNewsReader.sln
└── MyNewsReader
├── App.xaml
├── App.xaml.cs
├── AppShell.xaml
├── AppShell.xaml.cs
├── Constants.cs
├── MauiProgram.cs
├── Models
├── NewsFeed.cs
├── Rss.cs
└── Rss_Serializer.cs
├── MyNewsReader.csproj
├── Platforms
├── Android
│ ├── AndroidManifest.xml
│ ├── MainActivity.cs
│ ├── MainApplication.cs
│ └── Resources
│ │ └── values
│ │ └── colors.xml
├── MacCatalyst
│ ├── AppDelegate.cs
│ ├── Info.plist
│ └── Program.cs
├── Tizen
│ ├── Main.cs
│ └── tizen-manifest.xml
├── Windows
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── Package.appxmanifest
│ └── app.manifest
└── iOS
│ ├── AppDelegate.cs
│ ├── Info.plist
│ └── Program.cs
├── Properties
└── launchSettings.json
├── Resources
├── AppIcon
│ ├── appicon.svg
│ └── appiconfg.svg
├── Fonts
│ ├── OpenSans-Regular.ttf
│ └── OpenSans-Semibold.ttf
├── Images
│ └── dotnet_bot.svg
├── Raw
│ └── AboutAssets.txt
├── Splash
│ └── splash.svg
└── Styles
│ ├── Colors.xaml
│ └── Styles.xaml
├── Services
└── NewsFeedService.cs
├── ViewModels
├── AddNewsFeedViewModel.cs
├── ArticleViewModel.cs
├── BaseViewModel.cs
├── NewsFeedListViewModel.cs
└── NewsFeedViewModel.cs
└── Views
├── AddNewsFeed.xaml
├── AddNewsFeed.xaml.cs
├── ArticleContent.xaml
├── ArticleContent.xaml.cs
├── FeedContent.xaml
├── FeedContent.xaml.cs
├── MainPage.xaml
└── MainPage.xaml.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Jeffrey T. Fritz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MauiWorkshop
2 | This is a workshop that Fritz is teaching online, introducing how to build your first mobile application. In this workshop, you will learn how to build a simple news reader (RSS Reader) application.
3 |
4 | RSS is a format and standard that is used across the web to publish news updates and podcast feeds for syndication. Some news websites that publish RSS feeds include:
5 |
6 | - [Microsoft Dev Tools Blogs](https://devblogs.microsoft.com) - ([feed](https://devblogs.microsoft.com/landingpage/))
7 | - [The .NET SubReddit](https://www.reddit.com/r/dotnet) - ([feed](https://www.reddit.com/r/dotnet.rss))
8 | - [Dev.To #dotnet tagged content](https://dev.to/t/dotnet) = ([feed](https://dev.to/feed/tag/dotnet))
9 | - [NPR](https://npr.org) - ([feed](https://feeds.npr.org/1002/rss.xml))
10 | - [TechCrunch](https://techcrunch.com) - ([feed](https://techcrunch.com/feed/))
11 | - [The Verge](https://theverge.com) - ([feed](https://www.theverge.com/rss/index.xml))
12 | - [Hollywood Reporter Live Feed](https://www.hollywoodreporter.com/e/live-feed/) - ([feed](https://www.hollywoodreporter.com/e/live-feed/feed/))
13 | - [Rolling Stone](https://www.rollingstone.com/) - ([feed](https://www.rollingstone.com/feed/))
14 | - [My Blog](https://jeffreyfritz.com) - ([feed](https://jeffreyfritz.com/feed))
15 |
16 | We will build an application that will allow you to add newsfeeds like these, see notifications of new feed items, and view the content in a browser interface.
17 |
18 | ## Agenda
19 |
20 | | Topics |
21 | | ------ |
22 | | Module 0 - [Introducing .NET MAUI](doc/0-Introduction/README.md) |
23 | | Module 1 - [Layouts and Binding Data](doc/1-BindingData/README.md) |
24 | | Module 2 - [Making this look good with STYLE](doc/2-Style/README.md) |
25 | | Module 3 - [Saving Data Locally](doc/3-Data/README.md) |
26 | | Module 4 - [The MAUI Shell](doc/4-TheShell/README.md) |
27 | | Module 5 - [Navigation](doc/5-Navigation/README.md) |
28 | | Module 6 - [WebView and Hybrid](doc/6-WebView/README.md) |
29 |
30 |
31 | ## Huge thanks to our promotional sponsors!
32 |
33 | [](https://devexpress.com)
34 |
35 | FREE .NET MAUI UI Component Library by DevExpress. Includes a Data Grid, Chart, Scheduler, and more (for both Android and iOS).
36 |
37 | [](https://www.growthaccelerationpartners.com/)
38 |
39 | [](https://ironsoftware.com)
40 |
41 | Iron Software develop enterprise backend component libraries for .NET. They are focusing R&D on key, cross platform backend components for MAUI.
42 |
43 | [](bit.ly/3Zao3yz)
44 |
45 | Syncfusion delivers powerful MAUI data grids, charts, document processing libraries, and more for a complete developer toolkit.
46 |
47 | [](https://telerik.com)
--------------------------------------------------------------------------------
/doc/0-Introduction/img/0601_01_Architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/img/0601_01_Architecture.png
--------------------------------------------------------------------------------
/doc/0-Introduction/img/0601_02_AllPlatforms.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/img/0601_02_AllPlatforms.png
--------------------------------------------------------------------------------
/doc/0-Introduction/img/0601_03_CompileAotIos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/img/0601_03_CompileAotIos.png
--------------------------------------------------------------------------------
/doc/0-Introduction/img/0601_04_CompileAndroid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/img/0601_04_CompileAndroid.png
--------------------------------------------------------------------------------
/doc/0-Introduction/img/solution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/img/solution.png
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31903.59
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyNewsReader", "MyNewsReader\MyNewsReader.csproj", "{13F698DD-A76C-4AF9-9BAA-842F64F03CEC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
17 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.Build.0 = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(SolutionProperties) = preSolution
21 | HideSolutionNode = FALSE
22 | EndGlobalSection
23 | EndGlobal
24 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace MyNewsReader;
2 |
3 | public partial class App : Application {
4 | public App() {
5 | InitializeComponent();
6 |
7 | }
8 |
9 | protected override Window CreateWindow(IActivationState activationState) {
10 |
11 | if (DeviceInfo.Current.Idiom == DeviceIdiom.Desktop) {
12 |
13 | return new Window(new AppShell()) {
14 | Width = 600
15 | };
16 |
17 | }
18 |
19 | return new Window(new AppShell());
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/AppShell.xaml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/AppShell.xaml.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace MyNewsReader;
3 |
4 | public partial class AppShell : Shell
5 | {
6 | public AppShell()
7 | {
8 | InitializeComponent();
9 |
10 | RegisterForRoute();
11 | RegisterForRoute();
12 | RegisterForRoute();
13 |
14 | }
15 |
16 | protected void RegisterForRoute()
17 | {
18 |
19 | Routing.RegisterRoute(typeof(T).Name, typeof(T));
20 |
21 | }
22 |
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Constants.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MyNewsReader;
10 |
11 | public static class Constants {
12 | public const string DatabaseFilename = "MyNewsFeeds.db3";
13 |
14 | public const SQLite.SQLiteOpenFlags Flags =
15 | // open the database in read/write mode
16 | SQLite.SQLiteOpenFlags.ReadWrite |
17 | // create the database if it doesn't exist
18 | SQLite.SQLiteOpenFlags.Create |
19 | // enable multi-threaded database access
20 | SQLite.SQLiteOpenFlags.SharedCache;
21 |
22 | public static string DatabasePath =>
23 | Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
24 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/MauiProgram.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using MyNewsReader.Services;
3 | using MyNewsReader.ViewModels;
4 |
5 | namespace MyNewsReader;
6 |
7 | public static class MauiProgram
8 | {
9 | public static MauiApp CreateMauiApp()
10 | {
11 | var builder = MauiApp.CreateBuilder();
12 |
13 | builder
14 | .UseMauiApp()
15 | .ConfigureFonts(fonts =>
16 | {
17 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
18 | fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
19 | });
20 |
21 | #if DEBUG
22 | builder.Logging.AddDebug();
23 | #endif
24 |
25 | builder.Services
26 | .AddSingleton()
27 |
28 | .AddSingleton()
29 | .AddSingleton()
30 |
31 | .AddSingleton()
32 | .AddSingleton()
33 |
34 | .AddSingleton()
35 | .AddSingleton()
36 |
37 | .AddSingleton()
38 | .AddSingleton()
39 | ;
40 |
41 | return builder.Build();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Models/NewsFeed.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Models/Rss.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 |
10 | // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
11 | ///
12 | [System.SerializableAttribute()]
13 | [System.ComponentModel.DesignerCategoryAttribute("code")]
14 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
15 | [System.Xml.Serialization.XmlRootAttribute(ElementName = "rss")]
16 | public partial class Rss {
17 |
18 | private rssChannel channelField;
19 |
20 | private decimal versionField;
21 |
22 | ///
23 | public rssChannel channel {
24 | get {
25 | return this.channelField;
26 | }
27 | set {
28 | this.channelField = value;
29 | }
30 | }
31 |
32 | ///
33 | [System.Xml.Serialization.XmlAttributeAttribute()]
34 | public decimal version {
35 | get {
36 | return this.versionField;
37 | }
38 | set {
39 | this.versionField = value;
40 | }
41 | }
42 | }
43 |
44 | ///
45 | [System.SerializableAttribute()]
46 | [System.ComponentModel.DesignerCategoryAttribute("code")]
47 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
48 | public partial class rssChannel {
49 |
50 | private string titleField;
51 |
52 | private link linkField;
53 |
54 | private string link1Field;
55 |
56 | private string descriptionField;
57 |
58 | private string lastBuildDateField;
59 |
60 | private string languageField;
61 |
62 | private string updatePeriodField;
63 |
64 | private byte updateFrequencyField;
65 |
66 | private string generatorField;
67 |
68 | private rssChannelImage imageField;
69 |
70 | private rssChannelItem[] itemField;
71 |
72 | ///
73 | public string title {
74 | get {
75 | return this.titleField;
76 | }
77 | set {
78 | this.titleField = value;
79 | }
80 | }
81 |
82 | ///
83 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://www.w3.org/2005/Atom")]
84 | public link link {
85 | get {
86 | return this.linkField;
87 | }
88 | set {
89 | this.linkField = value;
90 | }
91 | }
92 |
93 | ///
94 | [System.Xml.Serialization.XmlElementAttribute("link")]
95 | public string link1 {
96 | get {
97 | return this.link1Field;
98 | }
99 | set {
100 | this.link1Field = value;
101 | }
102 | }
103 |
104 | ///
105 | public string description {
106 | get {
107 | return this.descriptionField;
108 | }
109 | set {
110 | this.descriptionField = value;
111 | }
112 | }
113 |
114 | ///
115 | public string lastBuildDate {
116 | get {
117 | return this.lastBuildDateField;
118 | }
119 | set {
120 | this.lastBuildDateField = value;
121 | }
122 | }
123 |
124 | ///
125 | public string language {
126 | get {
127 | return this.languageField;
128 | }
129 | set {
130 | this.languageField = value;
131 | }
132 | }
133 |
134 | ///
135 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
136 | public string updatePeriod {
137 | get {
138 | return this.updatePeriodField;
139 | }
140 | set {
141 | this.updatePeriodField = value;
142 | }
143 | }
144 |
145 | ///
146 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
147 | public byte updateFrequency {
148 | get {
149 | return this.updateFrequencyField;
150 | }
151 | set {
152 | this.updateFrequencyField = value;
153 | }
154 | }
155 |
156 | ///
157 | public string generator {
158 | get {
159 | return this.generatorField;
160 | }
161 | set {
162 | this.generatorField = value;
163 | }
164 | }
165 |
166 | ///
167 | public rssChannelImage image {
168 | get {
169 | return this.imageField;
170 | }
171 | set {
172 | this.imageField = value;
173 | }
174 | }
175 |
176 | public bool ImageIsVisible => !string.IsNullOrEmpty(image?.url);
177 |
178 | ///
179 | [System.Xml.Serialization.XmlElementAttribute("item")]
180 | public rssChannelItem[] item {
181 | get {
182 | return this.itemField;
183 | }
184 | set {
185 | this.itemField = value;
186 | }
187 | }
188 | }
189 |
190 | ///
191 | [System.SerializableAttribute()]
192 | [System.ComponentModel.DesignerCategoryAttribute("code")]
193 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.w3.org/2005/Atom")]
194 | [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.w3.org/2005/Atom", IsNullable = false)]
195 | public partial class link {
196 |
197 | private string hrefField;
198 |
199 | private string relField;
200 |
201 | private string typeField;
202 |
203 | ///
204 | [System.Xml.Serialization.XmlAttributeAttribute()]
205 | public string href {
206 | get {
207 | return this.hrefField;
208 | }
209 | set {
210 | this.hrefField = value;
211 | }
212 | }
213 |
214 | ///
215 | [System.Xml.Serialization.XmlAttributeAttribute()]
216 | public string rel {
217 | get {
218 | return this.relField;
219 | }
220 | set {
221 | this.relField = value;
222 | }
223 | }
224 |
225 | ///
226 | [System.Xml.Serialization.XmlAttributeAttribute()]
227 | public string type {
228 | get {
229 | return this.typeField;
230 | }
231 | set {
232 | this.typeField = value;
233 | }
234 | }
235 | }
236 |
237 | ///
238 | [System.SerializableAttribute()]
239 | [System.ComponentModel.DesignerCategoryAttribute("code")]
240 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
241 | public partial class rssChannelImage {
242 |
243 | private string urlField;
244 |
245 | private string titleField;
246 |
247 | private string linkField;
248 |
249 | private byte widthField;
250 |
251 | private byte heightField;
252 |
253 | ///
254 | public string url {
255 | get {
256 | return this.urlField;
257 | }
258 | set {
259 | this.urlField = value;
260 | }
261 | }
262 |
263 | ///
264 | public string title {
265 | get {
266 | return this.titleField;
267 | }
268 | set {
269 | this.titleField = value;
270 | }
271 | }
272 |
273 | ///
274 | public string link {
275 | get {
276 | return this.linkField;
277 | }
278 | set {
279 | this.linkField = value;
280 | }
281 | }
282 |
283 | ///
284 | public byte width {
285 | get {
286 | return this.widthField;
287 | }
288 | set {
289 | this.widthField = value;
290 | }
291 | }
292 |
293 | ///
294 | public byte height {
295 | get {
296 | return this.heightField;
297 | }
298 | set {
299 | this.heightField = value;
300 | }
301 | }
302 | }
303 |
304 | ///
305 | [System.SerializableAttribute()]
306 | [System.ComponentModel.DesignerCategoryAttribute("code")]
307 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
308 | public partial class rssChannelItem {
309 |
310 | private string titleField;
311 |
312 | private string linkField;
313 |
314 | private string commentsField;
315 |
316 | private string creatorField;
317 |
318 | private string pubDateField;
319 |
320 | private string[] categoryField;
321 |
322 | private rssChannelItemGuid guidField;
323 |
324 | private string descriptionField;
325 |
326 | private string encodedField;
327 |
328 | private string commentRssField;
329 |
330 | private byte comments1Field;
331 |
332 | ///
333 | public string title {
334 | get {
335 | return this.titleField;
336 | }
337 | set {
338 | this.titleField = value;
339 | }
340 | }
341 |
342 | ///
343 | public string link {
344 | get {
345 | return this.linkField;
346 | }
347 | set {
348 | this.linkField = value;
349 | }
350 | }
351 |
352 | ///
353 | public string comments {
354 | get {
355 | return this.commentsField;
356 | }
357 | set {
358 | this.commentsField = value;
359 | }
360 | }
361 |
362 | ///
363 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/dc/elements/1.1/")]
364 | public string creator {
365 | get {
366 | return this.creatorField;
367 | }
368 | set {
369 | this.creatorField = value;
370 | }
371 | }
372 |
373 | ///
374 | public string pubDate {
375 | get {
376 | return this.pubDateField;
377 | }
378 | set {
379 | this.pubDateField = value;
380 | }
381 | }
382 |
383 | public DateTimeOffset PublicationDate => DateTimeOffset.Parse(pubDate);
384 |
385 | ///
386 | [System.Xml.Serialization.XmlElementAttribute("category")]
387 | public string[] category {
388 | get {
389 | return this.categoryField;
390 | }
391 | set {
392 | this.categoryField = value;
393 | }
394 | }
395 |
396 | ///
397 | public rssChannelItemGuid guid {
398 | get {
399 | return this.guidField;
400 | }
401 | set {
402 | this.guidField = value;
403 | }
404 | }
405 |
406 | ///
407 | public string description {
408 | get {
409 | return this.descriptionField;
410 | }
411 | set {
412 | this.descriptionField = value;
413 | }
414 | }
415 |
416 | ///
417 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/content/")]
418 | public string encoded {
419 | get {
420 | return this.encodedField;
421 | }
422 | set {
423 | this.encodedField = value;
424 | }
425 | }
426 |
427 | ///
428 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://wellformedweb.org/CommentAPI/")]
429 | public string commentRss {
430 | get {
431 | return this.commentRssField;
432 | }
433 | set {
434 | this.commentRssField = value;
435 | }
436 | }
437 |
438 | ///
439 | [System.Xml.Serialization.XmlElementAttribute("comments", Namespace = "http://purl.org/rss/1.0/modules/slash/")]
440 | public byte comments1 {
441 | get {
442 | return this.comments1Field;
443 | }
444 | set {
445 | this.comments1Field = value;
446 | }
447 | }
448 | }
449 |
450 | ///
451 | [System.SerializableAttribute()]
452 | [System.ComponentModel.DesignerCategoryAttribute("code")]
453 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
454 | public partial class rssChannelItemGuid {
455 |
456 | private bool isPermaLinkField;
457 |
458 | private string valueField;
459 |
460 | ///
461 | [System.Xml.Serialization.XmlAttributeAttribute()]
462 | public bool isPermaLink {
463 | get {
464 | return this.isPermaLinkField;
465 | }
466 | set {
467 | this.isPermaLinkField = value;
468 | }
469 | }
470 |
471 | ///
472 | [System.Xml.Serialization.XmlTextAttribute()]
473 | public string Value {
474 | get {
475 | return this.valueField;
476 | }
477 | set {
478 | this.valueField = value;
479 | }
480 | }
481 | }
482 |
483 |
484 |
485 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Models/Rss_Serializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml.Serialization;
7 |
8 | namespace MyNewsReader.Models;
9 |
10 |
11 | public partial class Rss {
12 |
13 | static XmlSerializer _Serializer = new XmlSerializer(typeof(Rss));
14 | public static Rss Deserialize(string xml) {
15 |
16 | return (Rss)_Serializer.Deserialize(new System.IO.StringReader(xml));
17 |
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/MyNewsReader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0-android;net7.0-ios;net7.0-maccatalyst
5 | $(TargetFrameworks);net7.0-windows10.0.19041.0
6 |
7 |
8 | Exe
9 | MyNewsReader
10 | true
11 | true
12 | enable
13 |
14 |
15 | MyNewsReader
16 |
17 |
18 | com.companyname.mynewsreader
19 | 33e5262e-38a9-471b-856e-764021ce067c
20 |
21 |
22 | 1.0
23 | 1
24 |
25 | 11.0
26 | 13.1
27 | 21.0
28 | 10.0.17763.0
29 | 10.0.17763.0
30 | 6.5
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | FeedContent.xaml
61 |
62 |
63 |
64 |
65 |
66 | MSBuild:Compile
67 |
68 |
69 | MSBuild:Compile
70 |
71 |
72 | MSBuild:Compile
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Android/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content.PM;
3 | using Android.OS;
4 |
5 | namespace MyNewsReader;
6 |
7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
8 | public class MainActivity : MauiAppCompatActivity
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Android/MainApplication.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Runtime;
3 |
4 | namespace MyNewsReader;
5 |
6 | [Application]
7 | public class MainApplication : MauiApplication
8 | {
9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership)
10 | : base(handle, ownership)
11 | {
12 | }
13 |
14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
15 | }
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Android/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #512BD4
4 | #2B0B98
5 | #2B0B98
6 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/MacCatalyst/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/MacCatalyst/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIDeviceFamily
6 |
7 | 1
8 | 2
9 |
10 | UIRequiredDeviceCapabilities
11 |
12 | arm64
13 |
14 | UISupportedInterfaceOrientations
15 |
16 | UIInterfaceOrientationPortrait
17 | UIInterfaceOrientationLandscapeLeft
18 | UIInterfaceOrientationLandscapeRight
19 |
20 | UISupportedInterfaceOrientations~ipad
21 |
22 | UIInterfaceOrientationPortrait
23 | UIInterfaceOrientationPortraitUpsideDown
24 | UIInterfaceOrientationLandscapeLeft
25 | UIInterfaceOrientationLandscapeRight
26 |
27 | XSAppIconAssets
28 | Assets.xcassets/appicon.appiconset
29 |
30 |
31 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/MacCatalyst/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Tizen/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Maui;
3 | using Microsoft.Maui.Hosting;
4 |
5 | namespace MyNewsReader;
6 |
7 | class Program : MauiApplication
8 | {
9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
10 |
11 | static void Main(string[] args)
12 | {
13 | var app = new Program();
14 | app.Run(args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Tizen/tizen-manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | maui-appicon-placeholder
7 |
8 |
9 |
10 |
11 | http://tizen.org/privilege/internet
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Windows/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Windows/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 |
3 | // To learn more about WinUI, the WinUI project structure,
4 | // and more about our project templates, see: http://aka.ms/winui-project-info.
5 |
6 | namespace MyNewsReader.WinUI;
7 |
8 | ///
9 | /// Provides application-specific behavior to supplement the default Application class.
10 | ///
11 | public partial class App : MauiWinUIApplication
12 | {
13 | ///
14 | /// Initializes the singleton application object. This is the first line of authored code
15 | /// executed, and as such is the logical equivalent of main() or WinMain().
16 | ///
17 | public App()
18 | {
19 | this.InitializeComponent();
20 | }
21 |
22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Windows/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | $placeholder$
15 | User Name
16 | $placeholder$.png
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/Windows/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LSRequiresIPhoneOS
6 |
7 | UIDeviceFamily
8 |
9 | 1
10 | 2
11 |
12 | UIRequiredDeviceCapabilities
13 |
14 | arm64
15 |
16 | UISupportedInterfaceOrientations
17 |
18 | UIInterfaceOrientationPortrait
19 | UIInterfaceOrientationLandscapeLeft
20 | UIInterfaceOrientationLandscapeRight
21 |
22 | UISupportedInterfaceOrientations~ipad
23 |
24 | UIInterfaceOrientationPortrait
25 | UIInterfaceOrientationPortraitUpsideDown
26 | UIInterfaceOrientationLandscapeLeft
27 | UIInterfaceOrientationLandscapeRight
28 |
29 | XSAppIconAssets
30 | Assets.xcassets/appicon.appiconset
31 |
32 |
33 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Platforms/iOS/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Windows Machine": {
4 | "commandName": "MsixPackage",
5 | "nativeDebugging": false
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/AppIcon/appicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/AppIcon/appiconfg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/0-Introduction/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Images/dotnet_bot.svg:
--------------------------------------------------------------------------------
1 |
94 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Raw/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories). Deployment of the asset to your application
3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
4 |
5 |
6 |
7 | These files will be deployed with you package and will be accessible using Essentials:
8 |
9 | async Task LoadMauiAsset()
10 | {
11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
12 | using var reader = new StreamReader(stream);
13 |
14 | var contents = reader.ReadToEnd();
15 | }
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Splash/splash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Resources/Styles/Colors.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | #512BD4
8 | #DFD8F7
9 | #2B0B98
10 | White
11 | Black
12 | #E1E1E1
13 | #C8C8C8
14 | #ACACAC
15 | #919191
16 | #6E6E6E
17 | #404040
18 | #212121
19 | #141414
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | #F7B548
35 | #FFD590
36 | #FFE5B9
37 | #28C2D1
38 | #7BDDEF
39 | #C3F2F4
40 | #3E8EED
41 | #72ACF1
42 | #A7CBF6
43 |
44 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Services/NewsFeedService.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System.Net.Http.Headers;
4 |
5 | namespace MyNewsReader.Services;
6 |
7 | public class NewsFeedService
8 | {
9 |
10 | //private Dictionary _Feeds = new() {
11 | // { new NewsFeed("https://devblogs.microsoft.com/landingpage/", "Microsoft Dev Blogs"), null },
12 | // { new NewsFeed("http://feeds.feedburner.com/FritzOnTheWeb", "Jeff Fritz's Blog"), null },
13 | // { new NewsFeed("https://www.rollingstone.com/feed/", "Rolling Stone"), null },
14 | // { new NewsFeed("https://dev.to/feed/tag/dotnet", "Dev.To - #dotnet tag"), null }
15 | //};
16 |
17 | private HttpClient _Client;
18 |
19 | public NewsFeedService() {
20 | _Client = new HttpClient();
21 | var header = new ProductHeaderValue("MyNewsReader");
22 | var userAgent = new ProductInfoHeaderValue(header);
23 | _Client.DefaultRequestHeaders.UserAgent.Add(userAgent);
24 | }
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/ViewModels/AddNewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Services;
4 | using System.Diagnostics;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | public partial class AddNewsFeedViewModel : BaseViewModel {
9 |
10 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/ViewModels/ArticleViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 |
5 | namespace MyNewsReader.ViewModels;
6 |
7 |
8 | public partial class ArticleViewModel : BaseViewModel
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/ViewModels/BaseViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using MyNewsReader.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MyNewsReader.ViewModels;
11 |
12 | public partial class BaseViewModel
13 | {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/ViewModels/NewsFeedListViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Input;
2 | using MyNewsReader.Models;
3 | using MyNewsReader.Services;
4 | using System.Collections.ObjectModel;
5 | using System.Diagnostics;
6 |
7 | namespace MyNewsReader.ViewModels;
8 |
9 | public partial class NewsFeedListViewModel {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/ViewModels/NewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 | using MyNewsReader.Services;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | public partial class NewsFeedViewModel : BaseViewModel {
9 |
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/AddNewsFeed.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/AddNewsFeed.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class AddNewsFeed : ContentPage
6 | {
7 | public AddNewsFeed(AddNewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/ArticleContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/ArticleContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class ArticleContent : ContentPage
6 | {
7 | public ArticleContent(ArticleViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/FeedContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/FeedContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class FeedContent : ContentPage
6 | {
7 | public FeedContent(NewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/0-Introduction/src/MyNewsReader/Views/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class MainPage : ContentPage
6 | {
7 |
8 | public MainPage(NewsFeedListViewModel viewModel)
9 | {
10 | InitializeComponent();
11 | BindingContext = viewModel;
12 | }
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/README.md:
--------------------------------------------------------------------------------
1 | # Part 1 - Layouts and Binding Data
2 |
3 | We've got the idea for our application, a News Reader, but how do we assemble the user interface? We will want to use a layout to define the structure of our user-interface and we will want to bind data to views inside that layout to present the news feed content.
4 |
5 | ## Layouts
6 |
7 | 
8 |
9 | Layouts diagram courtesy of [Microsoft docs](https://learn.microsoft.com/dotnet/maui/user-interface/layouts/?view=net-maui-7.0)
10 |
11 | .NET Multi-platform App UI (.NET MAUI) layout classes allow you to arrange and group UI controls in your application. Choosing a layout class requires knowledge of how the layout positions its child elements, and how the layout sizes its child elements. In addition, it may be necessary to nest layouts to create your desired layout.
12 |
13 | For our application, we will prefer a StackLayout because not all RSS feeds are created with images and supporting content where a Grid or FlexLayout would help create a more interesting layout.
14 |
15 | 
16 |
17 | We'll start with a simple CollectionView that will use a StackLayout to present a list of feeds stacked vertically.
18 |
19 | ### Model
20 |
21 | We will be downloading details about the newsfeed and will need a class to represent it.
22 |
23 | RSS content will need to be downloaded from each feed and presented in the project. It's preferred to have an object model representing the RSS feed and we can create one from a feed avaialble on the web, like my blog at `https://jeffreyfritz.com/feed`
24 |
25 | We can easily convert the xml content with the **Paste Special** feature in Visual Studio. Copy the content of the XML file to your clipboard with Ctrl+C and select Edit - Paste Special - Paste XML as Classes. I have updated and made this content available for you as the RSS class in the project.
26 |
27 | ---
28 | > **NOTE**
29 |
30 | > After completing the demo, I realized there is a SyndicationFeed object and package available for .NET. I will update the content of the workshop to use this standard object instead of the serialization I wrote
31 |
32 | ---
33 |
34 | Let's also define the NewsFeed object we will work with in the project
35 |
36 | 1. Open `Models/NewsFeed.cs`
37 | 2. In `NewsFeed.cs`, copy/paste the properties:
38 |
39 | ```csharp
40 | public record NewsFeed(string SourceUrl, string Title) {
41 |
42 | public NewsFeed() : this(string.Empty, string.Empty) { }
43 |
44 | }
45 | ```
46 |
47 | ### Displaying Data
48 |
49 | We can display hard coded data of any data type in a `CollectionView` in our `MainPage.xaml`. This will allow us to build out our user interface by setting the `ItemTemplate` with some simple images and labels.
50 |
51 | We first need to add a new namespace at the top of the `MainPage.xaml`:
52 |
53 | ```xml
54 | xmlns:model="clr-namespace:MyNewsReader.Models"
55 | ```
56 |
57 | This will allow us to reference the Monkey class above for data binding purposes.
58 |
59 | Add the following into the MainPage.xaml's `ContentPage`:
60 |
61 | ```xml
62 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
81 |
82 |
83 |
84 | ```
85 |
86 |
87 |
88 | If we wanted to display the two strings vertically on top of each other, we could wrap two `Label` controls inside of a `VerticalStackLayout` and assign font sizes to stand out:
89 |
90 |
91 | ```xml
92 |
93 |
94 |
95 |
96 | ```
97 |
98 |
99 |
100 | ### Run the App
101 |
102 | Ensure that you have your machine setup to deploy and debug to the different platforms:
103 |
104 | * [Android Emulator Setup](https://docs.microsoft.com/dotnet/maui/android/emulator/device-manager)
105 | * [Windows setup for development](https://docs.microsoft.com/dotnet/maui/windows/setup)
106 |
107 | 1. In Visual Studio, set the Android or Windows app as the startup project by selecting the drop down in the debug menu and changing the `Framework`
108 |
109 |
110 | 
111 |
112 | 2. In Visual Studio, click the "Debug" button or Tools -> Start Debugging
113 | - If you are having any trouble, see the Setup guides for your runtime platform
114 |
--------------------------------------------------------------------------------
/doc/1-BindingData/img/layouts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/1-BindingData/img/layouts.png
--------------------------------------------------------------------------------
/doc/1-BindingData/img/results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/1-BindingData/img/results.png
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31903.59
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyNewsReader", "MyNewsReader\MyNewsReader.csproj", "{13F698DD-A76C-4AF9-9BAA-842F64F03CEC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
17 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.Build.0 = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(SolutionProperties) = preSolution
21 | HideSolutionNode = FALSE
22 | EndGlobalSection
23 | EndGlobal
24 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace MyNewsReader;
2 |
3 | public partial class App : Application {
4 | public App() {
5 | InitializeComponent();
6 |
7 | }
8 |
9 | protected override Window CreateWindow(IActivationState activationState) {
10 |
11 | if (DeviceInfo.Current.Idiom == DeviceIdiom.Desktop) {
12 |
13 | return new Window(new AppShell()) {
14 | Width = 600
15 | };
16 |
17 | }
18 |
19 | return new Window(new AppShell());
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/AppShell.xaml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/AppShell.xaml.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace MyNewsReader;
3 |
4 | public partial class AppShell : Shell
5 | {
6 | public AppShell()
7 | {
8 | InitializeComponent();
9 |
10 | RegisterForRoute();
11 | RegisterForRoute();
12 | RegisterForRoute();
13 |
14 | }
15 |
16 | protected void RegisterForRoute()
17 | {
18 |
19 | Routing.RegisterRoute(typeof(T).Name, typeof(T));
20 |
21 | }
22 |
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Constants.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MyNewsReader;
10 |
11 | public static class Constants {
12 | public const string DatabaseFilename = "MyNewsFeeds.db3";
13 |
14 | public const SQLite.SQLiteOpenFlags Flags =
15 | // open the database in read/write mode
16 | SQLite.SQLiteOpenFlags.ReadWrite |
17 | // create the database if it doesn't exist
18 | SQLite.SQLiteOpenFlags.Create |
19 | // enable multi-threaded database access
20 | SQLite.SQLiteOpenFlags.SharedCache;
21 |
22 | public static string DatabasePath =>
23 | Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
24 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/MauiProgram.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using MyNewsReader.Services;
3 | using MyNewsReader.ViewModels;
4 |
5 | namespace MyNewsReader;
6 |
7 | public static class MauiProgram
8 | {
9 | public static MauiApp CreateMauiApp()
10 | {
11 | var builder = MauiApp.CreateBuilder();
12 |
13 | builder
14 | .UseMauiApp()
15 | .ConfigureFonts(fonts =>
16 | {
17 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
18 | fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
19 | });
20 |
21 | #if DEBUG
22 | builder.Logging.AddDebug();
23 | #endif
24 |
25 | builder.Services
26 | .AddSingleton()
27 |
28 | .AddSingleton()
29 | .AddSingleton()
30 |
31 | .AddSingleton()
32 | .AddSingleton()
33 |
34 | .AddSingleton()
35 | .AddSingleton()
36 |
37 | .AddSingleton()
38 | .AddSingleton()
39 | ;
40 |
41 | return builder.Build();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Models/NewsFeed.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 | ///
10 | ///
11 | public record NewsFeed(string SourceUrl, string Title) {
12 | public NewsFeed() : this(string.Empty, string.Empty) {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Models/Rss.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 |
10 | // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
11 | ///
12 | [System.SerializableAttribute()]
13 | [System.ComponentModel.DesignerCategoryAttribute("code")]
14 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
15 | [System.Xml.Serialization.XmlRootAttribute(ElementName = "rss")]
16 | public partial class Rss {
17 |
18 | private rssChannel channelField;
19 |
20 | private decimal versionField;
21 |
22 | ///
23 | public rssChannel channel {
24 | get {
25 | return this.channelField;
26 | }
27 | set {
28 | this.channelField = value;
29 | }
30 | }
31 |
32 | ///
33 | [System.Xml.Serialization.XmlAttributeAttribute()]
34 | public decimal version {
35 | get {
36 | return this.versionField;
37 | }
38 | set {
39 | this.versionField = value;
40 | }
41 | }
42 | }
43 |
44 | ///
45 | [System.SerializableAttribute()]
46 | [System.ComponentModel.DesignerCategoryAttribute("code")]
47 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
48 | public partial class rssChannel {
49 |
50 | private string titleField;
51 |
52 | private link linkField;
53 |
54 | private string link1Field;
55 |
56 | private string descriptionField;
57 |
58 | private string lastBuildDateField;
59 |
60 | private string languageField;
61 |
62 | private string updatePeriodField;
63 |
64 | private byte updateFrequencyField;
65 |
66 | private string generatorField;
67 |
68 | private rssChannelImage imageField;
69 |
70 | private rssChannelItem[] itemField;
71 |
72 | ///
73 | public string title {
74 | get {
75 | return this.titleField;
76 | }
77 | set {
78 | this.titleField = value;
79 | }
80 | }
81 |
82 | ///
83 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://www.w3.org/2005/Atom")]
84 | public link link {
85 | get {
86 | return this.linkField;
87 | }
88 | set {
89 | this.linkField = value;
90 | }
91 | }
92 |
93 | ///
94 | [System.Xml.Serialization.XmlElementAttribute("link")]
95 | public string link1 {
96 | get {
97 | return this.link1Field;
98 | }
99 | set {
100 | this.link1Field = value;
101 | }
102 | }
103 |
104 | ///
105 | public string description {
106 | get {
107 | return this.descriptionField;
108 | }
109 | set {
110 | this.descriptionField = value;
111 | }
112 | }
113 |
114 | ///
115 | public string lastBuildDate {
116 | get {
117 | return this.lastBuildDateField;
118 | }
119 | set {
120 | this.lastBuildDateField = value;
121 | }
122 | }
123 |
124 | ///
125 | public string language {
126 | get {
127 | return this.languageField;
128 | }
129 | set {
130 | this.languageField = value;
131 | }
132 | }
133 |
134 | ///
135 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
136 | public string updatePeriod {
137 | get {
138 | return this.updatePeriodField;
139 | }
140 | set {
141 | this.updatePeriodField = value;
142 | }
143 | }
144 |
145 | ///
146 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
147 | public byte updateFrequency {
148 | get {
149 | return this.updateFrequencyField;
150 | }
151 | set {
152 | this.updateFrequencyField = value;
153 | }
154 | }
155 |
156 | ///
157 | public string generator {
158 | get {
159 | return this.generatorField;
160 | }
161 | set {
162 | this.generatorField = value;
163 | }
164 | }
165 |
166 | ///
167 | public rssChannelImage image {
168 | get {
169 | return this.imageField;
170 | }
171 | set {
172 | this.imageField = value;
173 | }
174 | }
175 |
176 | public bool ImageIsVisible => !string.IsNullOrEmpty(image?.url);
177 |
178 | ///
179 | [System.Xml.Serialization.XmlElementAttribute("item")]
180 | public rssChannelItem[] item {
181 | get {
182 | return this.itemField;
183 | }
184 | set {
185 | this.itemField = value;
186 | }
187 | }
188 | }
189 |
190 | ///
191 | [System.SerializableAttribute()]
192 | [System.ComponentModel.DesignerCategoryAttribute("code")]
193 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.w3.org/2005/Atom")]
194 | [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.w3.org/2005/Atom", IsNullable = false)]
195 | public partial class link {
196 |
197 | private string hrefField;
198 |
199 | private string relField;
200 |
201 | private string typeField;
202 |
203 | ///
204 | [System.Xml.Serialization.XmlAttributeAttribute()]
205 | public string href {
206 | get {
207 | return this.hrefField;
208 | }
209 | set {
210 | this.hrefField = value;
211 | }
212 | }
213 |
214 | ///
215 | [System.Xml.Serialization.XmlAttributeAttribute()]
216 | public string rel {
217 | get {
218 | return this.relField;
219 | }
220 | set {
221 | this.relField = value;
222 | }
223 | }
224 |
225 | ///
226 | [System.Xml.Serialization.XmlAttributeAttribute()]
227 | public string type {
228 | get {
229 | return this.typeField;
230 | }
231 | set {
232 | this.typeField = value;
233 | }
234 | }
235 | }
236 |
237 | ///
238 | [System.SerializableAttribute()]
239 | [System.ComponentModel.DesignerCategoryAttribute("code")]
240 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
241 | public partial class rssChannelImage {
242 |
243 | private string urlField;
244 |
245 | private string titleField;
246 |
247 | private string linkField;
248 |
249 | private byte widthField;
250 |
251 | private byte heightField;
252 |
253 | ///
254 | public string url {
255 | get {
256 | return this.urlField;
257 | }
258 | set {
259 | this.urlField = value;
260 | }
261 | }
262 |
263 | ///
264 | public string title {
265 | get {
266 | return this.titleField;
267 | }
268 | set {
269 | this.titleField = value;
270 | }
271 | }
272 |
273 | ///
274 | public string link {
275 | get {
276 | return this.linkField;
277 | }
278 | set {
279 | this.linkField = value;
280 | }
281 | }
282 |
283 | ///
284 | public byte width {
285 | get {
286 | return this.widthField;
287 | }
288 | set {
289 | this.widthField = value;
290 | }
291 | }
292 |
293 | ///
294 | public byte height {
295 | get {
296 | return this.heightField;
297 | }
298 | set {
299 | this.heightField = value;
300 | }
301 | }
302 | }
303 |
304 | ///
305 | [System.SerializableAttribute()]
306 | [System.ComponentModel.DesignerCategoryAttribute("code")]
307 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
308 | public partial class rssChannelItem {
309 |
310 | private string titleField;
311 |
312 | private string linkField;
313 |
314 | private string commentsField;
315 |
316 | private string creatorField;
317 |
318 | private string pubDateField;
319 |
320 | private string[] categoryField;
321 |
322 | private rssChannelItemGuid guidField;
323 |
324 | private string descriptionField;
325 |
326 | private string encodedField;
327 |
328 | private string commentRssField;
329 |
330 | private byte comments1Field;
331 |
332 | ///
333 | public string title {
334 | get {
335 | return this.titleField;
336 | }
337 | set {
338 | this.titleField = value;
339 | }
340 | }
341 |
342 | ///
343 | public string link {
344 | get {
345 | return this.linkField;
346 | }
347 | set {
348 | this.linkField = value;
349 | }
350 | }
351 |
352 | ///
353 | public string comments {
354 | get {
355 | return this.commentsField;
356 | }
357 | set {
358 | this.commentsField = value;
359 | }
360 | }
361 |
362 | ///
363 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/dc/elements/1.1/")]
364 | public string creator {
365 | get {
366 | return this.creatorField;
367 | }
368 | set {
369 | this.creatorField = value;
370 | }
371 | }
372 |
373 | ///
374 | public string pubDate {
375 | get {
376 | return this.pubDateField;
377 | }
378 | set {
379 | this.pubDateField = value;
380 | }
381 | }
382 |
383 | public DateTimeOffset PublicationDate => DateTimeOffset.Parse(pubDate);
384 |
385 | ///
386 | [System.Xml.Serialization.XmlElementAttribute("category")]
387 | public string[] category {
388 | get {
389 | return this.categoryField;
390 | }
391 | set {
392 | this.categoryField = value;
393 | }
394 | }
395 |
396 | ///
397 | public rssChannelItemGuid guid {
398 | get {
399 | return this.guidField;
400 | }
401 | set {
402 | this.guidField = value;
403 | }
404 | }
405 |
406 | ///
407 | public string description {
408 | get {
409 | return this.descriptionField;
410 | }
411 | set {
412 | this.descriptionField = value;
413 | }
414 | }
415 |
416 | ///
417 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/content/")]
418 | public string encoded {
419 | get {
420 | return this.encodedField;
421 | }
422 | set {
423 | this.encodedField = value;
424 | }
425 | }
426 |
427 | ///
428 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://wellformedweb.org/CommentAPI/")]
429 | public string commentRss {
430 | get {
431 | return this.commentRssField;
432 | }
433 | set {
434 | this.commentRssField = value;
435 | }
436 | }
437 |
438 | ///
439 | [System.Xml.Serialization.XmlElementAttribute("comments", Namespace = "http://purl.org/rss/1.0/modules/slash/")]
440 | public byte comments1 {
441 | get {
442 | return this.comments1Field;
443 | }
444 | set {
445 | this.comments1Field = value;
446 | }
447 | }
448 | }
449 |
450 | ///
451 | [System.SerializableAttribute()]
452 | [System.ComponentModel.DesignerCategoryAttribute("code")]
453 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
454 | public partial class rssChannelItemGuid {
455 |
456 | private bool isPermaLinkField;
457 |
458 | private string valueField;
459 |
460 | ///
461 | [System.Xml.Serialization.XmlAttributeAttribute()]
462 | public bool isPermaLink {
463 | get {
464 | return this.isPermaLinkField;
465 | }
466 | set {
467 | this.isPermaLinkField = value;
468 | }
469 | }
470 |
471 | ///
472 | [System.Xml.Serialization.XmlTextAttribute()]
473 | public string Value {
474 | get {
475 | return this.valueField;
476 | }
477 | set {
478 | this.valueField = value;
479 | }
480 | }
481 | }
482 |
483 |
484 |
485 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Models/Rss_Serializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml.Serialization;
7 |
8 | namespace MyNewsReader.Models;
9 |
10 |
11 | public partial class Rss {
12 |
13 | static XmlSerializer _Serializer = new XmlSerializer(typeof(Rss));
14 | public static Rss Deserialize(string xml) {
15 |
16 | return (Rss)_Serializer.Deserialize(new System.IO.StringReader(xml));
17 |
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/MyNewsReader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0-android;net7.0-ios;net7.0-maccatalyst
5 | $(TargetFrameworks);net7.0-windows10.0.19041.0
6 |
7 |
8 | Exe
9 | MyNewsReader
10 | true
11 | true
12 | enable
13 |
14 |
15 | MyNewsReader
16 |
17 |
18 | com.companyname.mynewsreader
19 | 33e5262e-38a9-471b-856e-764021ce067c
20 |
21 |
22 | 1.0
23 | 1
24 |
25 | 11.0
26 | 13.1
27 | 21.0
28 | 10.0.17763.0
29 | 10.0.17763.0
30 | 6.5
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | FeedContent.xaml
61 |
62 |
63 |
64 |
65 |
66 | MSBuild:Compile
67 |
68 |
69 | MSBuild:Compile
70 |
71 |
72 | MSBuild:Compile
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Android/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content.PM;
3 | using Android.OS;
4 |
5 | namespace MyNewsReader;
6 |
7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
8 | public class MainActivity : MauiAppCompatActivity
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Android/MainApplication.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Runtime;
3 |
4 | namespace MyNewsReader;
5 |
6 | [Application]
7 | public class MainApplication : MauiApplication
8 | {
9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership)
10 | : base(handle, ownership)
11 | {
12 | }
13 |
14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
15 | }
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Android/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #512BD4
4 | #2B0B98
5 | #2B0B98
6 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/MacCatalyst/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/MacCatalyst/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIDeviceFamily
6 |
7 | 1
8 | 2
9 |
10 | UIRequiredDeviceCapabilities
11 |
12 | arm64
13 |
14 | UISupportedInterfaceOrientations
15 |
16 | UIInterfaceOrientationPortrait
17 | UIInterfaceOrientationLandscapeLeft
18 | UIInterfaceOrientationLandscapeRight
19 |
20 | UISupportedInterfaceOrientations~ipad
21 |
22 | UIInterfaceOrientationPortrait
23 | UIInterfaceOrientationPortraitUpsideDown
24 | UIInterfaceOrientationLandscapeLeft
25 | UIInterfaceOrientationLandscapeRight
26 |
27 | XSAppIconAssets
28 | Assets.xcassets/appicon.appiconset
29 |
30 |
31 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/MacCatalyst/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Tizen/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Maui;
3 | using Microsoft.Maui.Hosting;
4 |
5 | namespace MyNewsReader;
6 |
7 | class Program : MauiApplication
8 | {
9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
10 |
11 | static void Main(string[] args)
12 | {
13 | var app = new Program();
14 | app.Run(args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Tizen/tizen-manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | maui-appicon-placeholder
7 |
8 |
9 |
10 |
11 | http://tizen.org/privilege/internet
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Windows/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Windows/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 |
3 | // To learn more about WinUI, the WinUI project structure,
4 | // and more about our project templates, see: http://aka.ms/winui-project-info.
5 |
6 | namespace MyNewsReader.WinUI;
7 |
8 | ///
9 | /// Provides application-specific behavior to supplement the default Application class.
10 | ///
11 | public partial class App : MauiWinUIApplication
12 | {
13 | ///
14 | /// Initializes the singleton application object. This is the first line of authored code
15 | /// executed, and as such is the logical equivalent of main() or WinMain().
16 | ///
17 | public App()
18 | {
19 | this.InitializeComponent();
20 | }
21 |
22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Windows/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | $placeholder$
15 | User Name
16 | $placeholder$.png
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/Windows/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LSRequiresIPhoneOS
6 |
7 | UIDeviceFamily
8 |
9 | 1
10 | 2
11 |
12 | UIRequiredDeviceCapabilities
13 |
14 | arm64
15 |
16 | UISupportedInterfaceOrientations
17 |
18 | UIInterfaceOrientationPortrait
19 | UIInterfaceOrientationLandscapeLeft
20 | UIInterfaceOrientationLandscapeRight
21 |
22 | UISupportedInterfaceOrientations~ipad
23 |
24 | UIInterfaceOrientationPortrait
25 | UIInterfaceOrientationPortraitUpsideDown
26 | UIInterfaceOrientationLandscapeLeft
27 | UIInterfaceOrientationLandscapeRight
28 |
29 | XSAppIconAssets
30 | Assets.xcassets/appicon.appiconset
31 |
32 |
33 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Platforms/iOS/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Windows Machine": {
4 | "commandName": "MsixPackage",
5 | "nativeDebugging": false
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/AppIcon/appicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/AppIcon/appiconfg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/1-BindingData/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/1-BindingData/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Images/dotnet_bot.svg:
--------------------------------------------------------------------------------
1 |
94 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Raw/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories). Deployment of the asset to your application
3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
4 |
5 |
6 |
7 | These files will be deployed with you package and will be accessible using Essentials:
8 |
9 | async Task LoadMauiAsset()
10 | {
11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
12 | using var reader = new StreamReader(stream);
13 |
14 | var contents = reader.ReadToEnd();
15 | }
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Splash/splash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Resources/Styles/Colors.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | #512BD4
8 | #DFD8F7
9 | #2B0B98
10 | White
11 | Black
12 | #E1E1E1
13 | #C8C8C8
14 | #ACACAC
15 | #919191
16 | #6E6E6E
17 | #404040
18 | #212121
19 | #141414
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | #F7B548
35 | #FFD590
36 | #FFE5B9
37 | #28C2D1
38 | #7BDDEF
39 | #C3F2F4
40 | #3E8EED
41 | #72ACF1
42 | #A7CBF6
43 |
44 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Services/NewsFeedService.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System.Net.Http.Headers;
4 |
5 | namespace MyNewsReader.Services;
6 |
7 | public class NewsFeedService
8 | {
9 |
10 | //private Dictionary _Feeds = new() {
11 | // { new NewsFeed("https://devblogs.microsoft.com/landingpage/", "Microsoft Dev Blogs"), null },
12 | // { new NewsFeed("http://feeds.feedburner.com/FritzOnTheWeb", "Jeff Fritz's Blog"), null },
13 | // { new NewsFeed("https://www.rollingstone.com/feed/", "Rolling Stone"), null },
14 | // { new NewsFeed("https://dev.to/feed/tag/dotnet", "Dev.To - #dotnet tag"), null }
15 | //};
16 |
17 | private HttpClient _Client;
18 |
19 | public NewsFeedService() {
20 | _Client = new HttpClient();
21 | var header = new ProductHeaderValue("MyNewsReader");
22 | var userAgent = new ProductInfoHeaderValue(header);
23 | _Client.DefaultRequestHeaders.UserAgent.Add(userAgent);
24 | }
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/ViewModels/AddNewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Services;
4 | using System.Diagnostics;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | public partial class AddNewsFeedViewModel : BaseViewModel {
9 |
10 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/ViewModels/ArticleViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 |
5 | namespace MyNewsReader.ViewModels;
6 |
7 |
8 | public partial class ArticleViewModel : BaseViewModel
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/ViewModels/BaseViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using MyNewsReader.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MyNewsReader.ViewModels;
11 |
12 | public partial class BaseViewModel
13 | {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/ViewModels/NewsFeedListViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Input;
2 | using MyNewsReader.Models;
3 | using MyNewsReader.Services;
4 | using System.Collections.ObjectModel;
5 | using System.Diagnostics;
6 |
7 | namespace MyNewsReader.ViewModels;
8 |
9 | public partial class NewsFeedListViewModel {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/ViewModels/NewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 | using MyNewsReader.Services;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | public partial class NewsFeedViewModel : BaseViewModel {
9 |
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/AddNewsFeed.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/AddNewsFeed.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class AddNewsFeed : ContentPage
6 | {
7 | public AddNewsFeed(AddNewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/ArticleContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/ArticleContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class ArticleContent : ContentPage
6 | {
7 | public ArticleContent(ArticleViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/FeedContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/FeedContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class FeedContent : ContentPage
6 | {
7 | public FeedContent(NewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/doc/1-BindingData/src/MyNewsReader/Views/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class MainPage : ContentPage
6 | {
7 |
8 | public MainPage(NewsFeedListViewModel viewModel)
9 | {
10 | InitializeComponent();
11 | BindingContext = viewModel;
12 | }
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/doc/2-Data/README.md:
--------------------------------------------------------------------------------
1 | ## Module 2: Creating and Saving Data Locally
2 |
3 | https://learn.microsoft.com/en-us/dotnet/maui/data-cloud/database-sqlite?view=net-maui-7.0
--------------------------------------------------------------------------------
/doc/4-TheShell/README.md:
--------------------------------------------------------------------------------
1 | ### Part 4 - The .NET MAUI Shell
2 |
3 | The [.NET MAUI Shell](https://learn.microsoft.com/dotnet/maui/fundamentals/shell) reduces the complexity of app development by providing the fundamental features that most apps require, including:
4 |
5 | - A single place to describe the visual hierarchy of an app.
6 | - A common navigation user experience.
7 | - A URI-based navigation scheme that permits navigation to any page in the app.
8 | - An integrated search handler.
9 |
10 | In the sample code provided, the application is started with a root object in `App.xaml` that initializes the application and hands off controls to `AppShell.xaml` where the Shell is defined:
11 |
12 | ```xml
13 |
14 |
18 |
19 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
37 | ```
38 |
39 | The shell defines the resources to be used in the application and the behavior of the navigation in the application. The `Shell.Resources` markup defines the styles to be applied to the contents of the Shell.
40 |
41 | The `ShellContent` element abstractly references the `MainPage` of our application using the `ContentTemplate` attribute and declaring that this content should be presented. A Route is assigned as well, so that we can navigate around the application.
42 |
43 | Check out the `Shell.NavBarIsVisible` attribute declaring that the navigation elements and title should be visible when the MainPage is displayed.
44 |
45 |
--------------------------------------------------------------------------------
/doc/img/devexpress-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/img/devexpress-light.png
--------------------------------------------------------------------------------
/doc/img/gap-mobilize-logo-lightbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/img/gap-mobilize-logo-lightbg.png
--------------------------------------------------------------------------------
/doc/img/iron-software-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/img/iron-software-light.png
--------------------------------------------------------------------------------
/doc/img/progress-telerik-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/img/progress-telerik-light.png
--------------------------------------------------------------------------------
/doc/img/syncfusion-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/doc/img/syncfusion-light.png
--------------------------------------------------------------------------------
/src/MyNewsReader.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31903.59
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyNewsReader", "MyNewsReader\MyNewsReader.csproj", "{13F698DD-A76C-4AF9-9BAA-842F64F03CEC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
17 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {13F698DD-A76C-4AF9-9BAA-842F64F03CEC}.Release|Any CPU.Build.0 = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(SolutionProperties) = preSolution
21 | HideSolutionNode = FALSE
22 | EndGlobalSection
23 | EndGlobal
24 |
--------------------------------------------------------------------------------
/src/MyNewsReader/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/MyNewsReader/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace MyNewsReader;
2 |
3 | public partial class App : Application {
4 | public App() {
5 | InitializeComponent();
6 |
7 | }
8 |
9 | protected override Window CreateWindow(IActivationState activationState) {
10 |
11 | if (DeviceInfo.Current.Idiom == DeviceIdiom.Desktop) {
12 |
13 | return new Window(new AppShell()) {
14 | Width = 600
15 | };
16 |
17 | }
18 |
19 | return new Window(new AppShell());
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/MyNewsReader/AppShell.xaml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/MyNewsReader/AppShell.xaml.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace MyNewsReader;
3 |
4 | public partial class AppShell : Shell
5 | {
6 | public AppShell()
7 | {
8 | InitializeComponent();
9 |
10 | RegisterForRoute();
11 | RegisterForRoute();
12 | RegisterForRoute();
13 |
14 | }
15 |
16 | protected void RegisterForRoute()
17 | {
18 |
19 | Routing.RegisterRoute(typeof(T).Name, typeof(T));
20 |
21 | }
22 |
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Constants.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MyNewsReader;
10 |
11 | public static class Constants {
12 | public const string DatabaseFilename = "MyNewsFeeds.db3";
13 |
14 | public const SQLite.SQLiteOpenFlags Flags =
15 | // open the database in read/write mode
16 | SQLite.SQLiteOpenFlags.ReadWrite |
17 | // create the database if it doesn't exist
18 | SQLite.SQLiteOpenFlags.Create |
19 | // enable multi-threaded database access
20 | SQLite.SQLiteOpenFlags.SharedCache;
21 |
22 | public static string DatabasePath =>
23 | Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
24 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/MauiProgram.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using MyNewsReader.Services;
3 | using MyNewsReader.ViewModels;
4 |
5 | namespace MyNewsReader;
6 |
7 | public static class MauiProgram
8 | {
9 | public static MauiApp CreateMauiApp()
10 | {
11 | var builder = MauiApp.CreateBuilder();
12 |
13 | builder
14 | .UseMauiApp()
15 | .ConfigureFonts(fonts =>
16 | {
17 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
18 | fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
19 | });
20 |
21 | #if DEBUG
22 | builder.Logging.AddDebug();
23 | #endif
24 |
25 | builder.Services
26 | .AddSingleton()
27 |
28 | .AddSingleton()
29 | .AddSingleton()
30 |
31 | .AddSingleton()
32 | .AddSingleton()
33 |
34 | .AddSingleton()
35 | .AddSingleton()
36 |
37 | .AddSingleton()
38 | .AddSingleton()
39 | ;
40 |
41 | return builder.Build();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Models/NewsFeed.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 | ///
10 | ///
11 | public record NewsFeed(string SourceUrl, string Title, DateTimeOffset? LastFetched, DateTimeOffset? LastUpdated) {
12 | public NewsFeed() : this(string.Empty, string.Empty, DateTimeOffset.MinValue, DateTimeOffset.MinValue) {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Models/Rss.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyNewsReader.Models;
8 |
9 |
10 | // NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
11 | ///
12 | [System.SerializableAttribute()]
13 | [System.ComponentModel.DesignerCategoryAttribute("code")]
14 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
15 | [System.Xml.Serialization.XmlRootAttribute(ElementName = "rss")]
16 | public partial class Rss {
17 |
18 | private rssChannel channelField;
19 |
20 | private decimal versionField;
21 |
22 | ///
23 | public rssChannel channel {
24 | get {
25 | return this.channelField;
26 | }
27 | set {
28 | this.channelField = value;
29 | }
30 | }
31 |
32 | ///
33 | [System.Xml.Serialization.XmlAttributeAttribute()]
34 | public decimal version {
35 | get {
36 | return this.versionField;
37 | }
38 | set {
39 | this.versionField = value;
40 | }
41 | }
42 | }
43 |
44 | ///
45 | [System.SerializableAttribute()]
46 | [System.ComponentModel.DesignerCategoryAttribute("code")]
47 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
48 | public partial class rssChannel {
49 |
50 | private string titleField;
51 |
52 | private link linkField;
53 |
54 | private string link1Field;
55 |
56 | private string descriptionField;
57 |
58 | private string lastBuildDateField;
59 |
60 | private string languageField;
61 |
62 | private string updatePeriodField;
63 |
64 | private byte updateFrequencyField;
65 |
66 | private string generatorField;
67 |
68 | private rssChannelImage imageField;
69 |
70 | private rssChannelItem[] itemField;
71 |
72 | ///
73 | public string title {
74 | get {
75 | return this.titleField;
76 | }
77 | set {
78 | this.titleField = value;
79 | }
80 | }
81 |
82 | ///
83 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://www.w3.org/2005/Atom")]
84 | public link link {
85 | get {
86 | return this.linkField;
87 | }
88 | set {
89 | this.linkField = value;
90 | }
91 | }
92 |
93 | ///
94 | [System.Xml.Serialization.XmlElementAttribute("link")]
95 | public string link1 {
96 | get {
97 | return this.link1Field;
98 | }
99 | set {
100 | this.link1Field = value;
101 | }
102 | }
103 |
104 | ///
105 | public string description {
106 | get {
107 | return this.descriptionField;
108 | }
109 | set {
110 | this.descriptionField = value;
111 | }
112 | }
113 |
114 | ///
115 | public string lastBuildDate {
116 | get {
117 | return this.lastBuildDateField;
118 | }
119 | set {
120 | this.lastBuildDateField = value;
121 | }
122 | }
123 |
124 | ///
125 | public string language {
126 | get {
127 | return this.languageField;
128 | }
129 | set {
130 | this.languageField = value;
131 | }
132 | }
133 |
134 | ///
135 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
136 | public string updatePeriod {
137 | get {
138 | return this.updatePeriodField;
139 | }
140 | set {
141 | this.updatePeriodField = value;
142 | }
143 | }
144 |
145 | ///
146 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
147 | public byte updateFrequency {
148 | get {
149 | return this.updateFrequencyField;
150 | }
151 | set {
152 | this.updateFrequencyField = value;
153 | }
154 | }
155 |
156 | ///
157 | public string generator {
158 | get {
159 | return this.generatorField;
160 | }
161 | set {
162 | this.generatorField = value;
163 | }
164 | }
165 |
166 | ///
167 | public rssChannelImage image {
168 | get {
169 | return this.imageField;
170 | }
171 | set {
172 | this.imageField = value;
173 | }
174 | }
175 |
176 | public bool ImageIsVisible => !string.IsNullOrEmpty(image?.url);
177 |
178 | ///
179 | [System.Xml.Serialization.XmlElementAttribute("item")]
180 | public rssChannelItem[] item {
181 | get {
182 | return this.itemField;
183 | }
184 | set {
185 | this.itemField = value;
186 | }
187 | }
188 | }
189 |
190 | ///
191 | [System.SerializableAttribute()]
192 | [System.ComponentModel.DesignerCategoryAttribute("code")]
193 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.w3.org/2005/Atom")]
194 | [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.w3.org/2005/Atom", IsNullable = false)]
195 | public partial class link {
196 |
197 | private string hrefField;
198 |
199 | private string relField;
200 |
201 | private string typeField;
202 |
203 | ///
204 | [System.Xml.Serialization.XmlAttributeAttribute()]
205 | public string href {
206 | get {
207 | return this.hrefField;
208 | }
209 | set {
210 | this.hrefField = value;
211 | }
212 | }
213 |
214 | ///
215 | [System.Xml.Serialization.XmlAttributeAttribute()]
216 | public string rel {
217 | get {
218 | return this.relField;
219 | }
220 | set {
221 | this.relField = value;
222 | }
223 | }
224 |
225 | ///
226 | [System.Xml.Serialization.XmlAttributeAttribute()]
227 | public string type {
228 | get {
229 | return this.typeField;
230 | }
231 | set {
232 | this.typeField = value;
233 | }
234 | }
235 | }
236 |
237 | ///
238 | [System.SerializableAttribute()]
239 | [System.ComponentModel.DesignerCategoryAttribute("code")]
240 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
241 | public partial class rssChannelImage {
242 |
243 | private string urlField;
244 |
245 | private string titleField;
246 |
247 | private string linkField;
248 |
249 | private byte widthField;
250 |
251 | private byte heightField;
252 |
253 | ///
254 | public string url {
255 | get {
256 | return this.urlField;
257 | }
258 | set {
259 | this.urlField = value;
260 | }
261 | }
262 |
263 | ///
264 | public string title {
265 | get {
266 | return this.titleField;
267 | }
268 | set {
269 | this.titleField = value;
270 | }
271 | }
272 |
273 | ///
274 | public string link {
275 | get {
276 | return this.linkField;
277 | }
278 | set {
279 | this.linkField = value;
280 | }
281 | }
282 |
283 | ///
284 | public byte width {
285 | get {
286 | return this.widthField;
287 | }
288 | set {
289 | this.widthField = value;
290 | }
291 | }
292 |
293 | ///
294 | public byte height {
295 | get {
296 | return this.heightField;
297 | }
298 | set {
299 | this.heightField = value;
300 | }
301 | }
302 | }
303 |
304 | ///
305 | [System.SerializableAttribute()]
306 | [System.ComponentModel.DesignerCategoryAttribute("code")]
307 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
308 | public partial class rssChannelItem {
309 |
310 | private string titleField;
311 |
312 | private string linkField;
313 |
314 | private string commentsField;
315 |
316 | private string creatorField;
317 |
318 | private string pubDateField;
319 |
320 | private string[] categoryField;
321 |
322 | private rssChannelItemGuid guidField;
323 |
324 | private string descriptionField;
325 |
326 | private string encodedField;
327 |
328 | private string commentRssField;
329 |
330 | private byte comments1Field;
331 |
332 | ///
333 | public string title {
334 | get {
335 | return this.titleField;
336 | }
337 | set {
338 | this.titleField = value;
339 | }
340 | }
341 |
342 | ///
343 | public string link {
344 | get {
345 | return this.linkField;
346 | }
347 | set {
348 | this.linkField = value;
349 | }
350 | }
351 |
352 | ///
353 | public string comments {
354 | get {
355 | return this.commentsField;
356 | }
357 | set {
358 | this.commentsField = value;
359 | }
360 | }
361 |
362 | ///
363 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/dc/elements/1.1/")]
364 | public string creator {
365 | get {
366 | return this.creatorField;
367 | }
368 | set {
369 | this.creatorField = value;
370 | }
371 | }
372 |
373 | ///
374 | public string pubDate {
375 | get {
376 | return this.pubDateField;
377 | }
378 | set {
379 | this.pubDateField = value;
380 | }
381 | }
382 |
383 | public DateTimeOffset PublicationDate => DateTimeOffset.Parse(pubDate);
384 |
385 | ///
386 | [System.Xml.Serialization.XmlElementAttribute("category")]
387 | public string[] category {
388 | get {
389 | return this.categoryField;
390 | }
391 | set {
392 | this.categoryField = value;
393 | }
394 | }
395 |
396 | ///
397 | public rssChannelItemGuid guid {
398 | get {
399 | return this.guidField;
400 | }
401 | set {
402 | this.guidField = value;
403 | }
404 | }
405 |
406 | ///
407 | public string description {
408 | get {
409 | return this.descriptionField;
410 | }
411 | set {
412 | this.descriptionField = value;
413 | }
414 | }
415 |
416 | ///
417 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://purl.org/rss/1.0/modules/content/")]
418 | public string encoded {
419 | get {
420 | return this.encodedField;
421 | }
422 | set {
423 | this.encodedField = value;
424 | }
425 | }
426 |
427 | ///
428 | [System.Xml.Serialization.XmlElementAttribute(Namespace = "http://wellformedweb.org/CommentAPI/")]
429 | public string commentRss {
430 | get {
431 | return this.commentRssField;
432 | }
433 | set {
434 | this.commentRssField = value;
435 | }
436 | }
437 |
438 | ///
439 | [System.Xml.Serialization.XmlElementAttribute("comments", Namespace = "http://purl.org/rss/1.0/modules/slash/")]
440 | public byte comments1 {
441 | get {
442 | return this.comments1Field;
443 | }
444 | set {
445 | this.comments1Field = value;
446 | }
447 | }
448 | }
449 |
450 | ///
451 | [System.SerializableAttribute()]
452 | [System.ComponentModel.DesignerCategoryAttribute("code")]
453 | [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
454 | public partial class rssChannelItemGuid {
455 |
456 | private bool isPermaLinkField;
457 |
458 | private string valueField;
459 |
460 | ///
461 | [System.Xml.Serialization.XmlAttributeAttribute()]
462 | public bool isPermaLink {
463 | get {
464 | return this.isPermaLinkField;
465 | }
466 | set {
467 | this.isPermaLinkField = value;
468 | }
469 | }
470 |
471 | ///
472 | [System.Xml.Serialization.XmlTextAttribute()]
473 | public string Value {
474 | get {
475 | return this.valueField;
476 | }
477 | set {
478 | this.valueField = value;
479 | }
480 | }
481 | }
482 |
483 |
484 |
485 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Models/Rss_Serializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml.Serialization;
7 |
8 | namespace MyNewsReader.Models;
9 |
10 |
11 | public partial class Rss {
12 |
13 | static XmlSerializer _Serializer = new XmlSerializer(typeof(Rss));
14 | public static Rss Deserialize(string xml) {
15 |
16 | return (Rss)_Serializer.Deserialize(new System.IO.StringReader(xml));
17 |
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/MyNewsReader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0-android;net7.0-ios;net7.0-maccatalyst
5 | $(TargetFrameworks);net7.0-windows10.0.19041.0
6 |
7 |
8 | Exe
9 | MyNewsReader
10 | true
11 | true
12 | enable
13 |
14 |
15 | MyNewsReader
16 |
17 |
18 | com.companyname.mynewsreader
19 | 33e5262e-38a9-471b-856e-764021ce067c
20 |
21 |
22 | 1.0
23 | 1
24 |
25 | 11.0
26 | 13.1
27 | 21.0
28 | 10.0.17763.0
29 | 10.0.17763.0
30 | 6.5
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | FeedContent.xaml
62 |
63 |
64 |
65 |
66 |
67 | MSBuild:Compile
68 |
69 |
70 | MSBuild:Compile
71 |
72 |
73 | MSBuild:Compile
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Android/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content.PM;
3 | using Android.OS;
4 |
5 | namespace MyNewsReader;
6 |
7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
8 | public class MainActivity : MauiAppCompatActivity
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Android/MainApplication.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Runtime;
3 |
4 | namespace MyNewsReader;
5 |
6 | [Application]
7 | public class MainApplication : MauiApplication
8 | {
9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership)
10 | : base(handle, ownership)
11 | {
12 | }
13 |
14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Android/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #512BD4
4 | #2B0B98
5 | #2B0B98
6 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/MacCatalyst/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/MacCatalyst/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIDeviceFamily
6 |
7 | 1
8 | 2
9 |
10 | UIRequiredDeviceCapabilities
11 |
12 | arm64
13 |
14 | UISupportedInterfaceOrientations
15 |
16 | UIInterfaceOrientationPortrait
17 | UIInterfaceOrientationLandscapeLeft
18 | UIInterfaceOrientationLandscapeRight
19 |
20 | UISupportedInterfaceOrientations~ipad
21 |
22 | UIInterfaceOrientationPortrait
23 | UIInterfaceOrientationPortraitUpsideDown
24 | UIInterfaceOrientationLandscapeLeft
25 | UIInterfaceOrientationLandscapeRight
26 |
27 | XSAppIconAssets
28 | Assets.xcassets/appicon.appiconset
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/MacCatalyst/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Tizen/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Maui;
3 | using Microsoft.Maui.Hosting;
4 |
5 | namespace MyNewsReader;
6 |
7 | class Program : MauiApplication
8 | {
9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
10 |
11 | static void Main(string[] args)
12 | {
13 | var app = new Program();
14 | app.Run(args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Tizen/tizen-manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | maui-appicon-placeholder
7 |
8 |
9 |
10 |
11 | http://tizen.org/privilege/internet
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Windows/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Windows/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 |
3 | // To learn more about WinUI, the WinUI project structure,
4 | // and more about our project templates, see: http://aka.ms/winui-project-info.
5 |
6 | namespace MyNewsReader.WinUI;
7 |
8 | ///
9 | /// Provides application-specific behavior to supplement the default Application class.
10 | ///
11 | public partial class App : MauiWinUIApplication
12 | {
13 | ///
14 | /// Initializes the singleton application object. This is the first line of authored code
15 | /// executed, and as such is the logical equivalent of main() or WinMain().
16 | ///
17 | public App()
18 | {
19 | this.InitializeComponent();
20 | }
21 |
22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Windows/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | $placeholder$
15 | User Name
16 | $placeholder$.png
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/Windows/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace MyNewsReader;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LSRequiresIPhoneOS
6 |
7 | UIDeviceFamily
8 |
9 | 1
10 | 2
11 |
12 | UIRequiredDeviceCapabilities
13 |
14 | arm64
15 |
16 | UISupportedInterfaceOrientations
17 |
18 | UIInterfaceOrientationPortrait
19 | UIInterfaceOrientationLandscapeLeft
20 | UIInterfaceOrientationLandscapeRight
21 |
22 | UISupportedInterfaceOrientations~ipad
23 |
24 | UIInterfaceOrientationPortrait
25 | UIInterfaceOrientationPortraitUpsideDown
26 | UIInterfaceOrientationLandscapeLeft
27 | UIInterfaceOrientationLandscapeRight
28 |
29 | XSAppIconAssets
30 | Assets.xcassets/appicon.appiconset
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Platforms/iOS/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace MyNewsReader;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Windows Machine": {
4 | "commandName": "MsixPackage",
5 | "nativeDebugging": false
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/AppIcon/appicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/AppIcon/appiconfg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/src/MyNewsReader/Resources/Fonts/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csharpfritz/MauiWorkshop/155283ecb14e12968cb677252514aead83b863b7/src/MyNewsReader/Resources/Fonts/OpenSans-Semibold.ttf
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Images/dotnet_bot.svg:
--------------------------------------------------------------------------------
1 |
94 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Raw/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories). Deployment of the asset to your application
3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
4 |
5 |
6 |
7 | These files will be deployed with you package and will be accessible using Essentials:
8 |
9 | async Task LoadMauiAsset()
10 | {
11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
12 | using var reader = new StreamReader(stream);
13 |
14 | var contents = reader.ReadToEnd();
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Splash/splash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Resources/Styles/Colors.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | #512BD4
8 | #DFD8F7
9 | #2B0B98
10 | White
11 | Black
12 | #E1E1E1
13 | #C8C8C8
14 | #ACACAC
15 | #919191
16 | #6E6E6E
17 | #404040
18 | #212121
19 | #141414
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | #F7B548
35 | #FFD590
36 | #FFE5B9
37 | #28C2D1
38 | #7BDDEF
39 | #C3F2F4
40 | #3E8EED
41 | #72ACF1
42 | #A7CBF6
43 |
44 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Services/NewsFeedService.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.Models;
2 | using SQLite;
3 | using System.Net.Http.Headers;
4 |
5 | namespace MyNewsReader.Services;
6 |
7 | public class NewsFeedService
8 | {
9 |
10 | private Dictionary _Feeds = new() {
11 | { new NewsFeed("https://devblogs.microsoft.com/landingpage/", "Microsoft Dev Blogs", null, null), null },
12 | { new NewsFeed("http://feeds.feedburner.com/FritzOnTheWeb", "Jeff Fritz's Blog", null, null), null },
13 | { new NewsFeed("https://www.rollingstone.com/feed/", "Rolling Stone", null, null), null },
14 | { new NewsFeed("https://dev.to/feed/tag/dotnet", "Dev.To - #dotnet tag", null, null), null }
15 | };
16 |
17 | private HttpClient _Client;
18 |
19 | public NewsFeedService() {
20 | _Client = new HttpClient();
21 | var header = new ProductHeaderValue("MyNewsReader");
22 | var userAgent = new ProductInfoHeaderValue(header);
23 | _Client.DefaultRequestHeaders.UserAgent.Add(userAgent);
24 | }
25 |
26 | public async Task> GetFeeds()
27 | {
28 |
29 | await Init();
30 | return await Database.Table().ToListAsync();
31 |
32 | //return _Feeds.Select(f => f.Key).ToArray();
33 |
34 | }
35 |
36 | public async Task AddFeed(string sourceUrl, string title)
37 | {
38 |
39 | await Init();
40 | var newFeed = new NewsFeed(sourceUrl, title, null, null);
41 | await Database.InsertAsync(newFeed);
42 |
43 | }
44 |
45 | public async Task GetContentForFeed(string sourceUrl)
46 | {
47 |
48 | //if (_Feeds.Any(f => f.Key.SourceUrl.Equals(sourceUrl, StringComparison.InvariantCultureIgnoreCase)) && _Feeds.First(f => f.Key.SourceUrl.Equals(sourceUrl, StringComparison.InvariantCultureIgnoreCase)).Value != null) {
49 | // return _Feeds.First(f => f.Key.SourceUrl.Equals(sourceUrl, StringComparison.InvariantCultureIgnoreCase)).Value;
50 | //}
51 |
52 | var response = await _Client.GetAsync(sourceUrl);
53 | if (!response.IsSuccessStatusCode) return new Rss();
54 | var content = Rss.Deserialize(await response.Content.ReadAsStringAsync());
55 |
56 | var newFeed = new NewsFeed {
57 | SourceUrl = sourceUrl,
58 | Title = content.channel.title,
59 | LastFetched = DateTimeOffset.Now,
60 | LastUpdated = string.IsNullOrEmpty(content.channel.lastBuildDate) ? DateTimeOffset.Now : DateTimeOffset.Parse(content.channel.lastBuildDate)
61 | };
62 |
63 | _Feeds[newFeed] = content;
64 |
65 | return content;
66 | }
67 |
68 | SQLiteAsyncConnection Database;
69 |
70 | async Task Init() {
71 | if (Database is not null)
72 | return;
73 |
74 | Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
75 | var result = await Database.CreateTableAsync();
76 | }
77 |
78 |
79 |
80 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/ViewModels/AddNewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Services;
4 | using System.Diagnostics;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | public partial class AddNewsFeedViewModel : BaseViewModel {
9 |
10 | private readonly NewsFeedService _Service;
11 |
12 | public AddNewsFeedViewModel(NewsFeedService service) {
13 |
14 | _Service = service;
15 |
16 | }
17 |
18 | [ObservableProperty]
19 | string sourceUrl;
20 |
21 | [ObservableProperty]
22 | string title;
23 |
24 | partial void OnSourceUrlChanged(string value) {
25 |
26 | try {
27 |
28 | var rss = _Service.GetContentForFeed(value).ContinueWith(continuationFunction: r => Title = r.Result.channel.title);
29 |
30 | } catch (Exception ex) {
31 |
32 | Debug.WriteLine($"Unable to add the feed {ex.Message}");
33 | Shell.Current.DisplayAlert("Error while adding feed", ex.Message, "OK");
34 |
35 | }
36 |
37 |
38 | }
39 |
40 | [RelayCommand]
41 | async Task SaveFeedAsync() {
42 |
43 | if (IsBusy) return;
44 |
45 | try {
46 |
47 | IsBusy = true;
48 | await _Service.AddFeed(sourceUrl, title);
49 |
50 | }
51 | catch (Exception ex) {
52 |
53 | Debug.WriteLine($"Unable to add the feed {ex.Message}");
54 | await Shell.Current.DisplayAlert("Error while adding feed", ex.Message, "OK");
55 |
56 | }
57 | finally {
58 |
59 | IsBusy = false;
60 |
61 | }
62 |
63 | await Shell.Current.GoToAsync("///MainPage", true);
64 |
65 | }
66 |
67 | [RelayCommand]
68 | async Task Cancel() {
69 |
70 | await Shell.Current.GoToAsync("///MainPage", true);
71 |
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/ViewModels/ArticleViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 |
5 | namespace MyNewsReader.ViewModels;
6 |
7 |
8 | [QueryProperty(nameof(Content), "Content")]
9 | public partial class ArticleViewModel : BaseViewModel
10 | {
11 |
12 | [ObservableProperty]
13 | rssChannelItem content;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyNewsReader/ViewModels/BaseViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using MyNewsReader.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MyNewsReader.ViewModels;
11 |
12 | public partial class BaseViewModel : ObservableObject
13 | {
14 |
15 | [ObservableProperty]
16 | bool isBusy;
17 |
18 | [ObservableProperty]
19 | string title;
20 |
21 | public bool IsNotBusy => !isBusy;
22 |
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/MyNewsReader/ViewModels/NewsFeedListViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Input;
2 | using MyNewsReader.Models;
3 | using MyNewsReader.Services;
4 | using System.Collections.ObjectModel;
5 | using System.Diagnostics;
6 |
7 | namespace MyNewsReader.ViewModels;
8 |
9 | public partial class NewsFeedListViewModel : BaseViewModel {
10 |
11 | private readonly NewsFeedService _Service;
12 |
13 | public ObservableCollection NewsFeeds { get; } = new();
14 |
15 | public NewsFeedListViewModel(NewsFeedService service) {
16 | _Service = service;
17 | GetFeedsAsync();
18 | }
19 |
20 |
21 | [RelayCommand]
22 | async Task GoToNewsFeed(NewsFeed feed) {
23 |
24 | if (feed == null) return;
25 |
26 | await Shell.Current.GoToAsync(nameof(FeedContent), true, new Dictionary {
27 | { "Feed", feed }
28 | });
29 |
30 | }
31 |
32 | [RelayCommand]
33 | async Task AddFeed() {
34 |
35 | await Shell.Current.GoToAsync(nameof(AddNewsFeed), true);
36 |
37 | }
38 |
39 | [RelayCommand]
40 | async Task GetFeedsAsync()
41 | {
42 |
43 | if (IsBusy) return;
44 |
45 | try {
46 | IsBusy = true;
47 | var feeds = await _Service.GetFeeds();
48 |
49 | if (NewsFeeds.Count != 0) NewsFeeds.Clear();
50 |
51 | foreach (var feed in feeds) {
52 | NewsFeeds.Add(feed);
53 | }
54 |
55 | } catch (Exception ex) {
56 |
57 | Debug.WriteLine($"Unable to get the feeds {ex.Message}");
58 | await Shell.Current.DisplayAlert("Error while fetching feeds", ex.Message, "OK");
59 |
60 | } finally {
61 |
62 | IsBusy = false;
63 |
64 | }
65 |
66 |
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/MyNewsReader/ViewModels/NewsFeedViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using MyNewsReader.Models;
4 | using MyNewsReader.Services;
5 |
6 | namespace MyNewsReader.ViewModels;
7 |
8 | [QueryProperty(nameof(Feed), "Feed")]
9 | public partial class NewsFeedViewModel : BaseViewModel {
10 |
11 | private readonly NewsFeedService _Service;
12 |
13 | public NewsFeedViewModel(NewsFeedService service) {
14 | _Service = service;
15 | }
16 |
17 | [ObservableProperty]
18 | NewsFeed feed;
19 |
20 | [ObservableProperty]
21 | Rss content;
22 |
23 | Task feedLoading;
24 | partial void OnFeedChanged(NewsFeed value) {
25 |
26 | Content = new();
27 | IsBusy = true;
28 |
29 | feedLoading = _Service.GetContentForFeed(value.SourceUrl).ContinueWith(r => {
30 | Content = r.Result;
31 | IsBusy = false;
32 | });
33 |
34 |
35 | }
36 |
37 |
38 | [RelayCommand]
39 | async Task GoToContent(rssChannelItem item) {
40 |
41 | if (item == null) return;
42 |
43 | await Shell.Current.GoToAsync(nameof(ArticleContent), true, new Dictionary {
44 | { "Content", item }
45 | });
46 |
47 | }
48 |
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/AddNewsFeed.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/AddNewsFeed.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class AddNewsFeed : ContentPage
6 | {
7 | public AddNewsFeed(AddNewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/ArticleContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/ArticleContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class ArticleContent : ContentPage
6 | {
7 | public ArticleContent(ArticleViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/FeedContent.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/FeedContent.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class FeedContent : ContentPage
6 | {
7 | public FeedContent(NewsFeedViewModel viewModel)
8 | {
9 | InitializeComponent();
10 | BindingContext = viewModel;
11 | }
12 | }
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 | 400
14 | 600
15 | 800
16 |
17 |
18 |
19 |
21 |
22 |
23 |
27 |
28 |
32 |
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
59 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/MyNewsReader/Views/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using MyNewsReader.ViewModels;
2 |
3 | namespace MyNewsReader;
4 |
5 | public partial class MainPage : ContentPage
6 | {
7 |
8 | public MainPage(NewsFeedListViewModel viewModel)
9 | {
10 | InitializeComponent();
11 | BindingContext = viewModel;
12 | }
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------