CINXE.COM
App vNext Blog — App vNext
<!doctype html> <html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml" lang="en-US" class="touch-styles"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- This is Squarespace. --><!-- appvnext --> <base href=""> <meta charset="utf-8" /> <title>App vNext Blog — App vNext</title> <meta http-equiv="Accept-CH" content="Sec-CH-UA-Platform-Version, Sec-CH-UA-Model" /><link rel="icon" type="image/x-icon" href="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1445429237674-KPIEGTBX83401CFR5ZJF/favicon.ico?format=100w"/> <link rel="canonical" href="https://www.appvnext.com/blog"/> <meta property="og:site_name" content="App vNext"/> <meta property="og:title" content="App vNext Blog — App vNext"/> <meta property="og:url" content="https://www.appvnext.com/blog"/> <meta property="og:type" content="website"/> <meta property="og:description" content=" Bright Ideas. Brought to life. "/> <meta property="og:image" content="http://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/5f47b2c1670f0560f11db2f1/1598534341675/avn.jpg?format=1500w"/> <meta property="og:image:width" content="1000"/> <meta property="og:image:height" content="338"/> <meta itemprop="name" content="App vNext Blog — App vNext"/> <meta itemprop="url" content="https://www.appvnext.com/blog"/> <meta itemprop="description" content=" Bright Ideas. Brought to life. "/> <meta itemprop="thumbnailUrl" content="http://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/5f47b2c1670f0560f11db2f1/1598534341675/avn.jpg?format=1500w"/> <link rel="image_src" href="http://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/5f47b2c1670f0560f11db2f1/1598534341675/avn.jpg?format=1500w" /> <meta itemprop="image" content="http://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/5f47b2c1670f0560f11db2f1/1598534341675/avn.jpg?format=1500w"/> <meta name="twitter:title" content="App vNext Blog — App vNext"/> <meta name="twitter:image" content="http://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/5f47b2c1670f0560f11db2f1/1598534341675/avn.jpg?format=1500w"/> <meta name="twitter:url" content="https://www.appvnext.com/blog"/> <meta name="twitter:card" content="summary"/> <meta name="twitter:description" content=" Bright Ideas. Brought to life. "/> <meta name="description" content=" Bright Ideas. Brought to life. " /> <link rel="preconnect" href="https://images.squarespace-cdn.com"> <link rel="preconnect" href="https://use.typekit.net" crossorigin> <link rel="preconnect" href="https://p.typekit.net" crossorigin> <script type="text/javascript" src="//use.typekit.net/ik/0OJJAakqVble1Kb06BVoFUN7IeVYZ22oCljaNKHsV5XfeT9ffFHN4UJLFRbh52jhWD9hw29hjRbaZQsKw2bDZ2bojRjXFc9aZsTtHKoySYghdcmtifoDSWmyScmDSeBRZPoRdhXCjWg3ico8pYb0jhNlOeB3SYZ0ZWyXO1FUiABkZWF3jAF8OcFzdPUCdhFydeyzSabCiaiaOcB3SYZ0ZWyXOcFzdPUaiaS0jWg3ico8pYb0SaBujW48Sagyjh90jhNlJ6U3ScNt-AuyOAozicIKIcBqdh48OAiyScBldhoqOWgkdkG4f5J7IMMjMkMfH6qJn3IbMg6IJMJ7fbKlMsMMeMj6MKG4fJCgIMMjgkMfH6qJn6IbMg6bJMJ7fbKwMsMMegI6MKG4fVwXIMIjgfMfqMeCziWkgb.js"></script> <script type="text/javascript">try{Typekit.load();}catch(e){}</script> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,400;0,700;1,400;1,700&family=Montserrat:ital,wght@0,700"><script type="text/javascript" crossorigin="anonymous" nomodule="nomodule" src="//assets.squarespace.com/@sqs/polyfiller/1.6/legacy.js"></script> <script type="text/javascript" crossorigin="anonymous" src="//assets.squarespace.com/@sqs/polyfiller/1.6/modern.js"></script> <script type="text/javascript">SQUARESPACE_ROLLUPS = {};</script> <script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/extract-css-runtime-e025f46c771aec3f-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-extract_css_runtime');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/extract-css-runtime-e025f46c771aec3f-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/extract-css-moment-js-vendor-b60fc53b12f6d6c5-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-extract_css_moment_js_vendor');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/extract-css-moment-js-vendor-b60fc53b12f6d6c5-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/cldr-resource-pack-c1d1ebd8f3f5f1d1-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-cldr_resource_pack');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/cldr-resource-pack-c1d1ebd8f3f5f1d1-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/common-vendors-stable-51880dc0d7a0158c-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-common_vendors_stable');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/common-vendors-stable-51880dc0d7a0158c-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/common-vendors-000883753df68ee3-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-common_vendors');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/common-vendors-000883753df68ee3-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/common-b824479f6199ba48-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-common');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/common-b824479f6199ba48-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/user-account-core-4109d46924bf9707-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-user_account_core');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/user-account-core-4109d46924bf9707-min.en-US.js" ></script><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].css = ["//assets.squarespace.com/universal/styles-compressed/user-account-core-32dec2abb821f895-min.en-US.css"]; })(SQUARESPACE_ROLLUPS, 'squarespace-user_account_core');</script> <link rel="stylesheet" type="text/css" href="//assets.squarespace.com/universal/styles-compressed/user-account-core-32dec2abb821f895-min.en-US.css"><script>(function(rollups, name) { if (!rollups[name]) { rollups[name] = {}; } rollups[name].js = ["//assets.squarespace.com/universal/scripts-compressed/performance-5c36eab9b8d6206e-min.en-US.js"]; })(SQUARESPACE_ROLLUPS, 'squarespace-performance');</script> <script crossorigin="anonymous" src="//assets.squarespace.com/universal/scripts-compressed/performance-5c36eab9b8d6206e-min.en-US.js" defer ></script><script data-name="static-context">Static = window.Static || {}; Static.SQUARESPACE_CONTEXT = {"betaFeatureFlags":["campaigns_import_discounts","marketing_automations","campaigns_new_image_layout_picker","i18n_beta_website_locales","marketing_landing_page","order_status_page_checkout_landing_enabled","campaigns_thumbnail_layout","campaigns_discount_section_in_automations","campaigns_discount_section_in_blasts","contacts_and_campaigns_redesign"],"facebookAppId":"314192535267336","facebookApiVersion":"v6.0","rollups":{"squarespace-announcement-bar":{"js":"//assets.squarespace.com/universal/scripts-compressed/announcement-bar-c1b3273291aa0cd6-min.en-US.js"},"squarespace-audio-player":{"css":"//assets.squarespace.com/universal/styles-compressed/audio-player-5d864aadea1060d1-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/audio-player-b944c8fc61ab17b6-min.en-US.js"},"squarespace-blog-collection-list":{"css":"//assets.squarespace.com/universal/styles-compressed/blog-collection-list-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/blog-collection-list-770996818a620a62-min.en-US.js"},"squarespace-calendar-block-renderer":{"css":"//assets.squarespace.com/universal/styles-compressed/calendar-block-renderer-b72d08ba4421f5a0-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/calendar-block-renderer-009604ccee80d9db-min.en-US.js"},"squarespace-chartjs-helpers":{"css":"//assets.squarespace.com/universal/styles-compressed/chartjs-helpers-96b256171ee039c1-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/chartjs-helpers-2142d2f8231d73c4-min.en-US.js"},"squarespace-comments":{"css":"//assets.squarespace.com/universal/styles-compressed/comments-01eecf660cca1c8d-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/comments-7d59f106bbdae530-min.en-US.js"},"squarespace-custom-css-popup":{"css":"//assets.squarespace.com/universal/styles-compressed/custom-css-popup-ddeacda396828f5b-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/custom-css-popup-e92442419afd2b18-min.en-US.js"},"squarespace-dialog":{"css":"//assets.squarespace.com/universal/styles-compressed/dialog-f9093f2d526b94df-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/dialog-eb8ffab4acf637a4-min.en-US.js"},"squarespace-events-collection":{"css":"//assets.squarespace.com/universal/styles-compressed/events-collection-b72d08ba4421f5a0-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/events-collection-673ded9ff661b090-min.en-US.js"},"squarespace-form-rendering-utils":{"js":"//assets.squarespace.com/universal/scripts-compressed/form-rendering-utils-4ec87c2a5f5b3ba5-min.en-US.js"},"squarespace-forms":{"css":"//assets.squarespace.com/universal/styles-compressed/forms-0afd3c6ac30bbab1-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/forms-9fc7266b65fdff92-min.en-US.js"},"squarespace-gallery-collection-list":{"css":"//assets.squarespace.com/universal/styles-compressed/gallery-collection-list-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/gallery-collection-list-91bf179640922449-min.en-US.js"},"squarespace-image-zoom":{"css":"//assets.squarespace.com/universal/styles-compressed/image-zoom-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/image-zoom-60f9abd094f76732-min.en-US.js"},"squarespace-pinterest":{"css":"//assets.squarespace.com/universal/styles-compressed/pinterest-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/pinterest-b5b0d5d77dbf37a4-min.en-US.js"},"squarespace-popup-overlay":{"css":"//assets.squarespace.com/universal/styles-compressed/popup-overlay-b742b752f5880972-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/popup-overlay-940ca78bacd094a9-min.en-US.js"},"squarespace-product-quick-view":{"css":"//assets.squarespace.com/universal/styles-compressed/product-quick-view-0ba5bac716923b8e-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/product-quick-view-40db4e652a5dbad0-min.en-US.js"},"squarespace-products-collection-item-v2":{"css":"//assets.squarespace.com/universal/styles-compressed/products-collection-item-v2-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/products-collection-item-v2-6d4891a84922348b-min.en-US.js"},"squarespace-products-collection-list-v2":{"css":"//assets.squarespace.com/universal/styles-compressed/products-collection-list-v2-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/products-collection-list-v2-dc2433d1c2d992d5-min.en-US.js"},"squarespace-search-page":{"css":"//assets.squarespace.com/universal/styles-compressed/search-page-90a67fc09b9b32c6-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/search-page-c862907fbdfb37a4-min.en-US.js"},"squarespace-search-preview":{"js":"//assets.squarespace.com/universal/scripts-compressed/search-preview-345ee71aaf5193d8-min.en-US.js"},"squarespace-simple-liking":{"css":"//assets.squarespace.com/universal/styles-compressed/simple-liking-701bf8bbc05ec6aa-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/simple-liking-0ce9c692006369a3-min.en-US.js"},"squarespace-social-buttons":{"css":"//assets.squarespace.com/universal/styles-compressed/social-buttons-95032e5fa98e47a5-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/social-buttons-095422a2cfdb76e8-min.en-US.js"},"squarespace-tourdates":{"css":"//assets.squarespace.com/universal/styles-compressed/tourdates-b4046463b72f34e2-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/tourdates-f8085f654b2ab443-min.en-US.js"},"squarespace-website-overlays-manager":{"css":"//assets.squarespace.com/universal/styles-compressed/website-overlays-manager-07ea5a4e004e6710-min.en-US.css","js":"//assets.squarespace.com/universal/scripts-compressed/website-overlays-manager-acef80c5a82235e9-min.en-US.js"}},"pageType":1,"website":{"id":"56056b47e4b043d49b646e7e","identifier":"appvnext","websiteType":1,"contentModifiedOn":1667577336332,"cloneable":false,"hasBeenCloneable":false,"developerMode":true,"siteStatus":{},"language":"en-US","timeZone":"America/New_York","machineTimeZoneOffset":-14400000,"timeZoneOffset":-14400000,"timeZoneAbbr":"EDT","siteTitle":"App vNext","fullSiteTitle":"App vNext Blog \u2014 App vNext","siteTagLine":"Bright Ideas. Brought to life.","siteDescription":"","location":{"mapZoom":11.0,"mapLat":41.3550227,"mapLng":-72.09863129999997,"markerLat":41.3550227,"markerLng":-72.09863129999997,"addressTitle":"App vNext","addressLine1":"","addressLine2":"","addressCountry":""},"logoImageId":"56277f5ce4b07271cf72e871","shareButtonOptions":{"3":true,"8":true,"2":true,"1":true,"6":true,"7":true,"4":true},"logoImageUrl":"//images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1445429084607-B9ZEZHBJS8AK9OEHIRT8/AvN_logo_white.png","authenticUrl":"https://www.appvnext.com","internalUrl":"https://appvnext.squarespace.com","baseUrl":"https://www.appvnext.com","primaryDomain":"www.appvnext.com","sslSetting":3,"isHstsEnabled":false,"typekitId":"","statsMigrated":false,"imageMetadataProcessingEnabled":false,"screenshotId":"12dcea68","captchaSettings":{"enabledForDonations":false},"showOwnerLogin":false},"websiteSettings":{"id":"56056b47e4b043d49b646e80","websiteId":"56056b47e4b043d49b646e7e","type":"Business","subjects":[],"country":"US","state":"FL","simpleLikingEnabled":true,"mobileInfoBarSettings":{"isContactEmailEnabled":false,"isContactPhoneNumberEnabled":false,"isLocationEnabled":false,"isBusinessHoursEnabled":false},"commentLikesAllowed":true,"commentAnonAllowed":true,"commentThreaded":true,"commentApprovalRequired":true,"commentAvatarsOn":true,"commentSortType":2,"commentFlagThreshold":0,"commentFlagsAllowed":true,"commentEnableByDefault":true,"commentDisableAfterDaysDefault":0,"disqusShortname":"appvnext","commentsEnabled":true,"contactPhoneNumber":"","businessHours":{"monday":{"text":"","ranges":[{}]},"tuesday":{"text":"","ranges":[{}]},"wednesday":{"text":"","ranges":[{}]},"thursday":{"text":"","ranges":[{}]},"friday":{"text":"","ranges":[{}]},"saturday":{"text":"","ranges":[{}]},"sunday":{"text":"","ranges":[{}]}},"storeSettings":{"returnPolicy":null,"termsOfService":null,"privacyPolicy":null,"expressCheckout":false,"continueShoppingLinkUrl":"/","useLightCart":false,"showNoteField":false,"shippingCountryDefaultValue":"US","billToShippingDefaultValue":false,"showShippingPhoneNumber":true,"isShippingPhoneRequired":false,"showBillingPhoneNumber":true,"isBillingPhoneRequired":false,"currenciesSupported":["USD","CAD","GBP","AUD","EUR","CHF"],"defaultCurrency":"USD","selectedCurrency":"USD","measurementStandard":1,"orderConfirmationInjectCode":"","showCustomCheckoutForm":false,"checkoutPageMarketingOptInEnabled":false,"enableMailingListOptInByDefault":true,"sameAsRetailLocation":false,"merchandisingSettings":{"scarcityEnabledOnProductItems":false,"scarcityEnabledOnProductBlocks":false,"scarcityMessageType":"DEFAULT_SCARCITY_MESSAGE","scarcityThreshold":10,"multipleQuantityAllowedForServices":true,"restockNotificationsEnabled":false,"restockNotificationsSuccessText":"","restockNotificationsMailingListSignUpEnabled":false,"relatedProductsEnabled":false,"relatedProductsOrdering":"random","soldOutVariantsDropdownDisabled":false,"productComposerOptedIn":false,"productComposerABTestOptedOut":false,"productReviewsEnabled":false,"displayImportedProductReviewsEnabled":false,"hasOptedToCollectNativeReviews":false},"minimumOrderSubtotalEnabled":false,"isLive":false,"multipleQuantityAllowedForServices":true},"useEscapeKeyToLogin":true,"ssBadgeType":1,"ssBadgePosition":4,"ssBadgeVisibility":1,"ssBadgeDevices":1,"pinterestOverlayOptions":{"mode":"disabled"},"userAccountsSettings":{"loginAllowed":false,"signupAllowed":false}},"cookieSettings":{"isCookieBannerEnabled":false,"isRestrictiveCookiePolicyEnabled":false,"cookieBannerText":"","cookieBannerTheme":"","cookieBannerVariant":"","cookieBannerPosition":"","cookieBannerCtaVariant":"","cookieBannerCtaText":"","cookieBannerAcceptType":"OPT_IN","cookieBannerOptOutCtaText":"","cookieBannerHasOptOut":false,"cookieBannerHasManageCookies":true,"cookieBannerManageCookiesLabel":"","cookieBannerSavedPreferencesText":"","cookieBannerSavedPreferencesLayout":"PILL"},"websiteCloneable":false,"collection":{"title":"App vNext Blog","id":"56056be1e4b0403251121b20","fullUrl":"/blog","type":1,"permissionType":1},"subscribed":false,"appDomain":"squarespace.com","templateTweakable":true,"tweakJSON":{"aspect-ratio":"Auto","banner-slideshow-controls":"Arrows","gallery-arrow-style":"No Background","gallery-aspect-ratio":"3:2 Standard","gallery-auto-crop":"true","gallery-autoplay":"false","gallery-design":"Grid","gallery-info-overlay":"Show on Hover","gallery-loop":"false","gallery-navigation":"Bullets","gallery-show-arrows":"true","gallery-transitions":"Fade","galleryArrowBackground":"rgba(34,34,34,1)","galleryArrowColor":"rgba(255,255,255,1)","galleryAutoplaySpeed":"3","galleryCircleColor":"rgba(255,255,255,1)","galleryInfoBackground":"rgba(0, 0, 0, .7)","galleryThumbnailSize":"100px","gridSize":"280px","gridSpacing":"10px","logoContainerWidth":"270px","product-gallery-auto-crop":"false","product-image-auto-crop":"true","siteTitleContainerWidth":"220px"},"templateId":"564e57ebe4b07f88bd496902","templateVersion":"7","pageFeatures":[1,2,4],"gmRenderKey":"QUl6YVN5Q0JUUk9xNkx1dkZfSUUxcjQ2LVQ0QWVUU1YtMGQ3bXk4","templateScriptsRootUrl":"https://static1.squarespace.com/static/ta/56056b47e4b043d49b646e7e/6/scripts/","impersonatedSession":false,"tzData":{"zones":[[-300,"US","E%sT",null]],"rules":{"US":[[1967,2006,null,"Oct","lastSun","2:00","0","S"],[1987,2006,null,"Apr","Sun>=1","2:00","1:00","D"],[2007,"max",null,"Mar","Sun>=8","2:00","1:00","D"],[2007,"max",null,"Nov","Sun>=1","2:00","0","S"]]}},"showAnnouncementBar":false,"recaptchaEnterpriseContext":{"recaptchaEnterpriseSiteKey":"6LdDFQwjAAAAAPigEvvPgEVbb7QBm-TkVJdDTlAv"},"i18nContext":{"timeZoneData":{"id":"America/New_York","name":"Eastern Time"}},"env":"PRODUCTION"};</script><script>Squarespace.load(window);</script> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="https://www.appvnext.com/blog?format=rss" /> <script type="application/ld+json">{"url":"https://www.appvnext.com","name":"App vNext","description":"","image":"//images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1445429084607-B9ZEZHBJS8AK9OEHIRT8/AvN_logo_white.png","@context":"http://schema.org","@type":"WebSite"}</script><script type="application/ld+json">{"legalName":"App vNext","address":"","email":"info@appvnext.com","telephone":"","sameAs":[],"@context":"http://schema.org","@type":"Organization"}</script><script type="application/ld+json">{"address":"","image":"https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/t/56277f5ce4b07271cf72e871/1667577336332/","name":"App vNext","openingHours":", , , , , , ","@context":"http://schema.org","@type":"LocalBusiness"}</script><link rel="stylesheet" type="text/css" href="https://static1.squarespace.com/static/sitecss/56056b47e4b043d49b646e7e/53/564e57ebe4b07f88bd496902/564e57ebe4b07f88bd49691f/6/site.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/agate.min.css"><script>Static.COOKIE_BANNER_CAPABLE = true;</script> <!-- End of Squarespace Headers --> <script>/* Must be below squarespace-headers */(function(){var e='ontouchstart'in window||navigator.msMaxTouchPoints;var t=document.documentElement;if(!e&&t){t.className=t.className.replace(/touch-styles/,'')}})() </script> </head> <body id="collection-56056be1e4b0403251121b20" class="transparent-header enable-nav-button nav-button-style-outline nav-button-corner-style-pill banner-button-style-solid banner-button-corner-style-pill banner-slideshow-controls-arrows meta-priority-date hide-blog-sidebar center-navigation--info gallery-design-grid aspect-ratio-auto lightbox-style-light gallery-navigation-bullets gallery-info-overlay-show-on-hover gallery-aspect-ratio-32-standard gallery-arrow-style-no-background gallery-transitions-fade gallery-show-arrows gallery-auto-crop product-list-titles-under product-list-alignment-center product-item-size-11-square product-image-auto-crop product-gallery-size-11-square show-product-price show-product-item-nav product-social-sharing event-show-past-events event-thumbnails event-thumbnail-size-32-standard event-date-label event-list-show-cats event-list-date event-list-time event-list-address event-icalgcal-links event-excerpts hide-opentable-icons opentable-style-dark newsletter-style-light small-button-style-solid small-button-shape-square medium-button-style-solid medium-button-shape-square large-button-style-solid large-button-shape-square button-style-solid button-corner-style-square tweak-product-quick-view-button-style-floating tweak-product-quick-view-button-position-bottom tweak-product-quick-view-lightbox-excerpt-display-truncate tweak-product-quick-view-lightbox-show-arrows tweak-product-quick-view-lightbox-show-close-button tweak-product-quick-view-lightbox-controls-weight-light native-currency-code-usd collection-56056be1e4b0403251121b20 collection-type-blog collection-layout-default view-list mobile-style-available has-banner-image general-page"> <input type="checkbox" name="mobile-nav-toggle" id="mobileNavToggle" class="mobile-nav-toggle-box hidden" /><a href="#" class="body-overlay"></a> <div class="sqs-announcement-bar-dropzone"></div> <div id="sidecarNav"> <div id="mobileNavWrapper" class="nav-wrapper" data-content-field="navigation-mobileNav"> <nav id="mobileNavigation"> <div class="collection homepage"> <a href="/"> Home </a> </div> <div class="collection"> <a href="/training"> Training </a> </div> <div class="collection active"> <a href="/blog"> App vNext Blog </a> </div> </nav> </div> </div> <div id="siteWrapper" class="clearfix"> <div class="sqs-cart-dropzone"></div> <header id="header" class="show-on-scroll" data-offset-el=".index-section" data-offset-behavior="bottom" role="banner"> <div class="header-inner"> <div id="logoWrapper" class="wrapper" data-content-field="site-title"> <h1 id="logoImage"><a href="/"><img src="//images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1445429084607-B9ZEZHBJS8AK9OEHIRT8/AvN_logo_white.png?format=1500w" alt="App vNext" /></a></h1> </div><!-- comment the linebreak between these two elements because science --><label for="mobileNavToggle" class="mobile-nav-toggle-label"><div class="top-bar"></div><div class="middle-bar"></div><div class="bottom-bar"></div></label><label for="mobileNavToggle" class="mobile-nav-toggle-label fixed-nav-toggle-label"><div class="top-bar"></div><div class="middle-bar"></div><div class="bottom-bar"></div></label><!-- comment the linebreak between these two elements because science --><div id="headerNav"><div id="mainNavWrapper" class="nav-wrapper" data-content-field="navigation-mainNav"> <nav id="mainNavigation" data-content-field="navigation-mainNav"> <div class="collection homepage"> <a href="/"> Home </a> </div> <div class="collection"> <a href="/training"> Training </a> </div> <div class="collection active"> <a href="/blog"> App vNext Blog </a> </div> </nav> </div> <!-- style below blocks out the mobile nav toggle only when nav is loaded --> <style>.mobile-nav-toggle-label { display: inline-block !important; }</style> </div> </div> </header> <div class="sqs-featured-posts-gallery"> <div class="gallery-wrapper"> <!--FULL SIZE IMAGES--> <div class="slides-controls"> <div class="posts"> <!--SLIDE--> <div class="post no-main-image content-fill" data-slide-id="61630fb3137d373f83a59c66"> <div class="title-desc-wrapper"> <time class="dt-published published post-date" datetime="2021-10-10" pubdate>October 10, 2021</time> <div class="post-category"></div> <div class="post-author">Carl Franklin</div> <div class="post-title"><a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm">Build MAUI Apps in an Azure VM</a></div> <a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" class="view-post"></a> </div> <div class="color-overlay"></div> </div> <!--SLIDE--> <div class="post no-main-image content-fill" data-slide-id="5f9dfcce0e604768db0d1ab5"> <div class="title-desc-wrapper"> <time class="dt-published published post-date" datetime="2020-10-31" pubdate>October 31, 2020</time> <div class="post-category"></div> <div class="post-author">Carl Franklin</div> <div class="post-title"><a href="/blog/2020/10/31/mvvm-in-blazor">MVVM in Blazor!</a></div> <a href="/blog/2020/10/31/mvvm-in-blazor" class="view-post"></a> </div> <div class="color-overlay"></div> </div> <!--SLIDE--> <div class="post no-main-image content-fill" data-slide-id="5f77757d71a9992d995d9d44"> <div class="title-desc-wrapper"> <time class="dt-published published post-date" datetime="2020-10-02" pubdate>October 2, 2020</time> <div class="post-category"></div> <div class="post-author">Carl Franklin</div> <div class="post-title"><a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application">Add Blazor to an Existing MVC Application</a></div> <a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" class="view-post"></a> </div> <div class="color-overlay"></div> </div> </div> <!--PREVIOUS/NEXT CONTROLS--> <span class="arrow previous-slide"></span> <span class="arrow next-slide"></span> <!--CIRCLES--> <div class="circles gallery-nav"> <span class="circle"></span> <span class="circle"></span> <span class="circle"></span> </div> </div> </div> </div> <main id="page" role="main"> <!-- comment the linebreak between these two elements because science --><!-- comment the linebreak between these two elements because science --><div id="content" class="main-content" data-content-field="main-content" data-collection-id="56056be1e4b0403251121b20" data-edit-main-image="Banner"> <div class="blog-list"> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-1" id="article-625a11a393566e1ac83b9134" data-item-id="625a11a393566e1ac83b9134"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2022-04-15" pubdate><a href="/blog/2022/4/15/tips-to-avoid-getting-scammed-online" class="entry-dateline-link">April 15, 2022</a></time> <time class="dt-updated updated" datetime="2022-04-15"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2022/4/15/tips-to-avoid-getting-scammed-online" class="u-url" rel="bookmark">Tips to avoid getting scammed online.</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2022-04-15" pubdate><a href="/blog/2022/4/15/tips-to-avoid-getting-scammed-online" class="entry-dateline-link">April 15, 2022</a></time> <time class="dt-updated updated" datetime="2022-04-15"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1650069980315" id="item-625a11a393566e1ac83b9134"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" data-border-radii="{"topLeft":{"unit":"px","value":0.0},"topRight":{"unit":"px","value":0.0},"bottomLeft":{"unit":"px","value":0.0},"bottomRight":{"unit":"px","value":0.0}}" id="block-b53665797b0ac60d21c7"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">The most common way people get scammed is by social engineering. I learned a few simple scam-prevention techniques from my co-hosts on the podcast <a href="https://securitythisweek.com">Security This Week</a>, where we teach security topics through the lens of current hacks. On the podcast, I'm the dumb guy asking questions, the other two are red-hat security professionals.</p><p class="" style="white-space:pre-wrap;">If you get an email or a text from what looks like a company that you pay money to on a regular basis, and they offer a link you can click to do whatever they are asking you to do, don't click it.</p><p class="" style="white-space:pre-wrap;">If it's an email, log on to the official website, open your account settings, and check to see if there are any notifications for you. If you don't see anything there, you're probably okay. You can confirm with the company by sending them an email or using their contact page. If it's a text, a Facebook message, or any other kind of instant message, don't click on it. </p><p class="" style="white-space:pre-wrap;">You might get an email from one of your credit card companies that has a number you should call right away. Don't call it. Instead, call the number on the back of your card and ask them if they sent you this email.</p><p class="" style="white-space:pre-wrap;">If you get a Facebook message from one of your friends with a request that seems out of character, like "Hey, Carl. I'm stuck in Miami because I got ripped off and I need $500 for a plane ticket home" - Don't answer it. Contact your friend by another means and ask them if they sent you a message.</p><p class="" style="white-space:pre-wrap;">Also, sometimes you'll get Facebook messages from friends you haven't heard from in a while, and the message is just something like "how are you?" or "hi..." they have probably been hacked. Again, contact them using another method, and confirm.</p><p class="" style="white-space:pre-wrap;">If you get an email or a message from a friend that has an attachment and the words "check this out!" or some other non-descript request, do not click it. This is how Ransomware gets installed.</p><p class="" style="white-space:pre-wrap;">The best way to confirm the identity of a friend online is to ask them a question that only they can answer, or if that seems too weird, just ask them to call you. Even if a scammer has your number, it won't be your friend on the other line.</p><p class="" style="white-space:pre-wrap;">Another tip is to NEVER scan a QR code in a public location that doesn't look official. Anyone can create a QR image from a URL, even if that URL points to a website that can infect your phone. Don't ever scan a QR code printed on a sheet of paper and taped to the wall, a bus, a billboard, or any other public property. </p><p class="" style="white-space:pre-wrap;">Wifi routers are another problem. You need to make sure your WIFI router is always up to date with the current firmware. I have one that automatically updates itself.</p><p class="" style="white-space:pre-wrap;">Also, you should have two WIFI networks, one that your computers connect to using a strong password, and another one, a GUEST network, that has no password, and is not connected to any computers or printers. That's the one you should use for your household devices (NEST, etc) and give to your guests.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2022/4/15/tips-to-avoid-getting-scammed-online" class="sqs-comment-link sqs-disqus-comment-link" data-id="625a11a393566e1ac83b9134"></a> <span class="sqs-simple-like" data-item-id="625a11a393566e1ac83b9134" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/625a11a393566e1ac83b9134/1650070069385/" data-record-type="1" data-full-url="/blog/2022/4/15/tips-to-avoid-getting-scammed-online" data-title="Tips to avoid getting scammed online."></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-2 featured" id="article-61630fb3137d373f83a59c66" data-item-id="61630fb3137d373f83a59c66"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2021-10-10" pubdate><a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" class="entry-dateline-link">October 10, 2021</a></time> <time class="dt-updated updated" datetime="2021-10-12"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" class="u-url" rel="bookmark">Build MAUI Apps in an Azure VM</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2021-10-10" pubdate><a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" class="entry-dateline-link">October 10, 2021</a></time> <time class="dt-updated updated" datetime="2021-10-12"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1633888201720" id="item-61630fb3137d373f83a59c66"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633881998918_16928"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1922px;" > <a class=" sqs-block-image-link " href="/s/maui01.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:57.85639953613281%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png" data-image-dimensions="1922x1112" data-image-focal-point="0.5,0.5" alt="maui01.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png" width="1922" height="1112" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893061618-8ATNMW2F4WLWW74PHQ7M/maui01.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-5e9d7a1fee3dacb10957"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">I love using a cloud-based virtual machine as my developer machine. A moment’s thought will reveal why. A VM is isolated. You can set up a developer environment on a per-project basis. You can access it from anywhere, untethering yourself from your office chair. You can also turn it off when not using to save money.</p><p class="" style="white-space:pre-wrap;">As great as that sounds, and it is, I have never used a VM (at least in Azure) for mobile development. It’s hard to find the right configuration for an Android emulator to work. Besides, I would rather use my device to test while I’m developing.</p><p class="" style="white-space:pre-wrap;">In this article I’ll show you how to install the latest MAUI preview with .NET 6 RC1 and Visual Studio 2022 Preview. I’ll also give a resource that will allow you to develop using your local Android device, even though Visual Studio is running in the cloud.</p><p class="" style="white-space:pre-wrap;">I’m not covering iOS devices in this blog post because I could not get them working using this workflow. Might be just me.</p><p class="" style="white-space:pre-wrap;">You can watch a video of me building this solution from start to finish at <a href="https://youtu.be/Pmr5nDJ1Q7w">https://youtu.be/Pmr5nDJ1Q7w</a> </p><h3 style="white-space:pre-wrap;">Step 1: Create a new Azure VM</h3><p class="" style="white-space:pre-wrap;">For this, I chose the “Standard D8s v3 (8 vcpus, 32 GiB memory)” SKU, which would cost about $280/month if you ran it 24/7. If you’re only using it for 8 hours a day the cost would be less than $100/month.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633881998918_19669"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1906px;" > <a class=" sqs-block-image-link " href="/s/maui02.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.29590606689453%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png" data-image-dimensions="1906x1073" data-image-focal-point="0.5,0.5" alt="maui02.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png" width="1906" height="1073" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633893179285-O92D0OYAU5EX7AEHW0G4/maui02.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633881998918_21100"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 2: Install Visual Studio 2022 Preview</h3><p class="" style="white-space:pre-wrap;">You can download the Visual Studio Installer for VS2022 from <a href="https://visualstudio.microsoft.com/vs/preview/">https://visualstudio.microsoft.com/vs/preview/</a> Follow the instructions to install from <a href="https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation">https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation</a> </p><p class="" style="white-space:pre-wrap;">Let’s go over the critical install steps:</p><p class="" style="white-space:pre-wrap;">First, make sure you check off <strong>ASP.NET and web development</strong>,<strong> Universal Windows Platform development</strong>, and <strong>Mobile development with .NET</strong>.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633888202619_5006"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1056px;" > <a class=" sqs-block-image-link " href="/s/maui03.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:60.5113639831543%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png" data-image-dimensions="1056x639" data-image-focal-point="0.5,0.5" alt="maui03.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png" width="1056" height="639" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888485862-6YXRCVGMMBEQKZZC2JXM/maui03.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633888202619_6388"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, in the <strong>Installation details</strong> > <strong>Mobile development with .NET</strong> section of the installation window, check the <strong>.NET MAUI (Preview)</strong> optional workload, as it is NOT selected by default.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633888202619_6898"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:394px;" > <a class=" sqs-block-image-link " href="/s/maui04.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:114.97461700439453%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png" data-image-dimensions="394x453" data-image-focal-point="0.5,0.5" alt="maui04.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png" width="394" height="453" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633888618338-SE0H2C922S57UUDYV76V/maui04.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633888202619_7966"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 3: Install requirements for Android devices.</h3><p class="" style="white-space:pre-wrap;">First, install Microsoft OpenJDK version 11 from <a href="https://docs.microsoft.com/en-us/java/openjdk/download">https://docs.microsoft.com/en-us/java/openjdk/download</a>. </p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633888202619_20845"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1906px;" > <a class=" sqs-block-image-link " href="/s/maui05.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.29590606689453%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png" data-image-dimensions="1906x1073" data-image-focal-point="0.5,0.5" alt="maui05.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png" width="1906" height="1073" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889133480-KZ27C380OPKSEHXDWJ9R/maui05.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633888202619_21900"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, follow the instructions at <a href="https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation">https://docs.microsoft.com/en-us/dotnet/maui/get-started/installation</a> to install the Android 12 (API 31) SDK. </p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633888202619_22501"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1906px;" > <a class=" sqs-block-image-link " href="/s/maui06.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.29590606689453%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png" data-image-dimensions="1906x1073" data-image-focal-point="0.5,0.5" alt="maui06.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png" width="1906" height="1073" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633889368067-J9EUF3JNRIA4S4D2OQMN/maui06.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633888202619_35659"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 4: Install Usb for Remote Desktop</h3><p class="" style="white-space:pre-wrap;">This is the secret sauce. Download and install USB for Remote Desktop from <a href="https://www.usb-over-network.com/usb-for-remote-desktop.html">https://www.usb-over-network.com/usb-for-remote-desktop.html</a>. </p><p class="" style="white-space:pre-wrap;">You must install two apps: <strong>Server</strong>, which you install in your VM, and <strong>Workstation</strong>, which you install on your local machine. </p><p class="" style="white-space:pre-wrap;">They have a free trial, but the cost for a single developer is only 12 bucks a month. If you plan to use a VM for regular mobile work, this is money well spent.</p><p class="" style="white-space:pre-wrap;">Once installed, connect to your VM with RDP, but make sure you add your Android device to the list of local resources.</p><p class="" style="white-space:pre-wrap;">To accomplish this, run <strong>Remote Desktop Connection</strong>, and click the <strong>Show Options</strong> dropdown</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_14447"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:407px;" > <a class=" sqs-block-image-link " href="/s/maui08.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:62.16216278076172%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png" data-image-dimensions="407x253" data-image-focal-point="0.5,0.5" alt="maui08.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png" width="407" height="253" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890202866-O9Q912M5AVOK95WJNU0T/maui08.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633889622305_15508"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Select the <strong>Local Resources</strong> tab, then click the <strong>More</strong> button.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_12792"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:407px;" > <a class=" sqs-block-image-link " href="/s/maui07.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:116.46192169189453%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png" data-image-dimensions="407x474" data-image-focal-point="0.5,0.5" alt="maui07.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png" width="407" height="474" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890045781-ACBW4CH11I99RZDGD16E/maui07.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633889622305_13866"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Expand <strong>Other supported Plug and Play (PnP) devices</strong> and select your device from the list.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_16003"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:407px;" > <a class=" sqs-block-image-link " href="/s/maui09.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:93.3660888671875%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png" data-image-dimensions="407x380" data-image-focal-point="0.5,0.5" alt="maui98.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png" width="407" height="380" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633890992662-5NMKF33VEFK1H44769JS/maui98.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633889622305_39700"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 5: Install your Android device’s USB driver</h3><p class="" style="white-space:pre-wrap;">Download and install your Android device’s USB Driver in the VM</p><p class="" style="white-space:pre-wrap;">If you use a Samsung phone, you can install the USB driver from <a href="https://developer.samsung.com/mobile/android-usb-driver.html">https://developer.samsung.com/mobile/android-usb-driver.html</a> </p><p class="" style="white-space:pre-wrap;">Be careful. There are lots of unofficial sites offering plenty of clickbait. Make sure you get yours from the source website.</p><h3 style="white-space:pre-wrap;">STEP 6: Connect to VM and register your Android Device</h3><p class="" style="white-space:pre-wrap;">Once connected to your VM, expand the <strong>Show hidden icons</strong> button in the toolbar, and then right-click on the <strong>FabulaTech USB for Remote Desktop</strong> icon</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_36372"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:625px;" > <a class=" sqs-block-image-link " href="/s/maui10.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:79.5199966430664%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png" data-image-dimensions="625x497" data-image-focal-point="0.5,0.5" alt="maui10.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png" width="625" height="497" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891225734-Q8A12FXM1TFV5QOGDO8X/maui10.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633889622305_37856"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Select your device from the list presented to you.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_38352"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:468px;" > <a class=" sqs-block-image-link " href="/s/maui11.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:92.0940170288086%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png" data-image-dimensions="468x431" data-image-focal-point="0.5,0.5" alt="maui11.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png" width="468" height="431" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633891442477-FF84SCVWWJUK4NDH9CYD/maui11.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633889622305_40421"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 7: Build and test your first MAUI app</h3><p class="" style="white-space:pre-wrap;">Follow steps 1 through 4 from <a href="https://docs.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=windows">https://docs.microsoft.com/en-us/dotnet/maui/get-started/first-app?pivots=windows</a> to build your first MAUI app. It is important to wait patiently until all of the dependencies have been recognized. </p><h3 style="white-space:pre-wrap;">Step 8: Select the Android Platform and your local device.</h3><p class="" style="white-space:pre-wrap;">If you wait long enough, Visual Studio should see your device and set it as the default target of deployment. If it doesn’t see your device, make sure it is selected by <strong>USB Remote Desktop</strong> as per Step 6.</p><p class="" style="white-space:pre-wrap;">This dropdown is how you select not only the device but the platform that you’re going to deploy to and run. </p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633907482075_99496"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1922px;" > <a class=" sqs-block-image-link " href="/s/maui12a.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:57.85639953613281%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png" data-image-dimensions="1922x1112" data-image-focal-point="0.5,0.5" alt="maui12.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png" width="1922" height="1112" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908048306-KPG3IUOAATOAE7AM80YP/maui12.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1633907482075_99839"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;">Step 9: Run the app</h3><p class="" style="white-space:pre-wrap;">You must be patient! Go get a cup of coffee. It may take a while. Don’t stop it before it has a chance to complete. Wait until it gets past the .NET logo splash screen. </p><p class="" style="white-space:pre-wrap;">Yes, Hot Reload will probably not work due to the latency, but you get to see your app <strong><em>on your phone</em></strong> even though you’re developing in the cloud.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1633889622305_70478"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1922px;" > <a class=" sqs-block-image-link " href="/s/maui13a.png" target="_blank" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:57.85639953613281%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png" data-image-dimensions="1922x1112" data-image-focal-point="0.5,0.5" alt="maui13.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png" width="1922" height="1112" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1633908102001-2OMBZW1P2O2OT6VPNBQ3/maui13.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </a> </figure> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" class="sqs-comment-link sqs-disqus-comment-link" data-id="61630fb3137d373f83a59c66"></a> <span class="sqs-simple-like" data-item-id="61630fb3137d373f83a59c66" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/61630fb3137d373f83a59c66/1634061744839/" data-record-type="1" data-full-url="/blog/2021/10/10/build-maui-apps-in-an-azure-vm" data-title="Build MAUI Apps in an Azure VM"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-3 featured" id="article-5f9dfcce0e604768db0d1ab5" data-item-id="5f9dfcce0e604768db0d1ab5"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-31" pubdate><a href="/blog/2020/10/31/mvvm-in-blazor" class="entry-dateline-link">October 31, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-31"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/31/mvvm-in-blazor" class="u-url" rel="bookmark">MVVM in Blazor!</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-31" pubdate><a href="/blog/2020/10/31/mvvm-in-blazor" class="entry-dateline-link">October 31, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-31"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1604189920939" id="item-5f9dfcce0e604768db0d1ab5"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1604189378292_15586"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="mvvm.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1604189606444-VHN8WLCCXMOPU8DI3U3A/mvvm.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-963aa1f9c6371a9d38d9"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Blazor’s robust component model makes MVVM easy peasy. In these two episodes of <a href="https://blazortrain.com" target="_blank">BlazorTrain</a> - a YouTube weekly by <a href="https://twitter.com/carlfranklin" target="_blank">Carl Franklin</a> - you’ll learn the WHY and how of using ViewModels in Blazor.</p><p class="" style="white-space:pre-wrap;"><a href="https://youtu.be/pxNCogNSPm0" target="_blank">Episode 24</a> shows you how to convert an existing Blazor CRUD form into MVVM.</p><p class="" style="white-space:pre-wrap;"><a href="https://youtu.be/PUm9P_pJZV0" target="_blank">Episode 25</a> shows advanced MVVM patterns for more real-world scenarios.</p><p class="" style="white-space:pre-wrap;">For a list of all episodes go to <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a></p><p class="" style="white-space:pre-wrap;">Sponsored by <a href="https://www.devexpress.com/blazor/" target="_blank">DevExpress</a>, offering professional components for Blazor.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/31/mvvm-in-blazor" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f9dfcce0e604768db0d1ab5"></a> <span class="sqs-simple-like" data-item-id="5f9dfcce0e604768db0d1ab5" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f9dfcce0e604768db0d1ab5/1604190273011/" data-record-type="1" data-full-url="/blog/2020/10/31/mvvm-in-blazor" data-title="MVVM in Blazor!"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-4" id="article-5f812605f3350a0e2629381f" data-item-id="5f812605f3350a0e2629381f"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-09" pubdate><a href="/blog/2020/10/9/daniel-roth-demos-whats-new-in-net-5-blazor-with-carl-franklin" class="entry-dateline-link">October 9, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-09"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/9/daniel-roth-demos-whats-new-in-net-5-blazor-with-carl-franklin" class="u-url" rel="bookmark">Daniel Roth demos what's new in .NET 5 Blazor with Carl Franklin</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-09" pubdate><a href="/blog/2020/10/9/daniel-roth-demos-whats-new-in-net-5-blazor-with-carl-franklin" class="entry-dateline-link">October 9, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-09"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1602299831450" id="item-5f812605f3350a0e2629381f"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image sqs-col-6 span-6 float float-left" data-block-type="5" id="block-yui_3_17_2_1_1602299381014_15651"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:800px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg" data-image-dimensions="800x450" data-image-focal-point="0.5,0.5" alt="screenshot.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg" width="800" height="450" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1602299626911-K5NLZ63U9Z73P1VXLP1D/screenshot.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-caec6da0f54c1bee621e"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Carl Franklin talks to Microsoft’s Daniel Roth about what’s new in .NET 5 for Blazor.</p><p class="" style="white-space:pre-wrap;"><a href="https://youtu.be/Yp1WmfjadMw" target="_blank">Click here for the video. </a></p><p class="" style="white-space:pre-wrap;"><a href="https://blazortrain.com" target="_blank">Click here for more episodes of BlazorTain</a></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/9/daniel-roth-demos-whats-new-in-net-5-blazor-with-carl-franklin" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f812605f3350a0e2629381f"></a> <span class="sqs-simple-like" data-item-id="5f812605f3350a0e2629381f" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f812605f3350a0e2629381f/1602299830810/" data-record-type="1" data-full-url="/blog/2020/10/9/daniel-roth-demos-whats-new-in-net-5-blazor-with-carl-franklin" data-title="Daniel Roth demos what's new in .NET 5 Blazor with Carl Franklin"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-5" id="article-5f778c902cbbb95554e901fa" data-item-id="5f778c902cbbb95554e901fa"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/blazor-binding-and-event-handling" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/2/blazor-binding-and-event-handling" class="u-url" rel="bookmark">Blazor Binding and Event Handling</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/blazor-binding-and-event-handling" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1601670494348" id="item-5f778c902cbbb95554e901fa"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1601664367065_117843"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="09-screen-shot.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601670465229-JB51KDWLYNJT5ZPPGEOD/09-screen-shot.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-3722d0417065b31f1c99"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">In <a href="https://youtu.be/kg6JBEFu_kc" target="_blank">episode 9</a> of <a href="https://blazortrain.com" target="_blank">BlazorTrain</a> I explain binding in Blazor to a professional MVC developer who had never seen Blazor under the hood. For more episodes check out <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/2/blazor-binding-and-event-handling" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f778c902cbbb95554e901fa"></a> <span class="sqs-simple-like" data-item-id="5f778c902cbbb95554e901fa" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f778c902cbbb95554e901fa/1601670492655/" data-record-type="1" data-full-url="/blog/2020/10/2/blazor-binding-and-event-handling" data-title="Blazor Binding and Event Handling"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-6" id="article-5f778ad1ecf7ee10e9975fa1" data-item-id="5f778ad1ecf7ee10e9975fa1"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/use-javascript-in-blazor-to-auto-resize-pages" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/2/use-javascript-in-blazor-to-auto-resize-pages" class="u-url" rel="bookmark">Use JavaScript in Blazor to Auto-Resize Pages</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/use-javascript-in-blazor-to-auto-resize-pages" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1601670048142" id="item-5f778ad1ecf7ee10e9975fa1"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1601664367065_83632"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="13-screen-shot.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669955487-A2425TE9DEXL5CXI7A5R/13-screen-shot.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-d92ab769ccc5b503920c"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">In <a href="https://youtu.be/tOA3DZPI4I0" target="_blank">episode 13</a> of <a href="https://blazortrain.com" target="_blank">BlazorTrain</a> I show how to use JavaScript Interop in Blazor to automatically resize the divs on your page as the user resizes the browser. It’s a good example of calling JavaScript from C# and also calling into C# from JavaScript. More episodes at <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a></p><p class="" data-rte-preserve-empty="true" style="white-space:pre-wrap;"></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/2/use-javascript-in-blazor-to-auto-resize-pages" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f778ad1ecf7ee10e9975fa1"></a> <span class="sqs-simple-like" data-item-id="5f778ad1ecf7ee10e9975fa1" data-like-count="1"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f778ad1ecf7ee10e9975fa1/1601670208818/" data-record-type="1" data-full-url="/blog/2020/10/2/use-javascript-in-blazor-to-auto-resize-pages" data-title="Use JavaScript in Blazor to Auto-Resize Pages"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-7" id="article-5f7788172d0ee66430ed4721" data-item-id="5f7788172d0ee66430ed4721"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/simplifying-data-access-in-blazor" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/2/simplifying-data-access-in-blazor" class="u-url" rel="bookmark">Simplifying Data Access in Blazor</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/simplifying-data-access-in-blazor" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1601669594334" id="item-5f7788172d0ee66430ed4721"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1601664367065_66144"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="screen-shot.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601669472052-5WSE48W125Y4D8X7AZFA/screen-shot.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-ae14eb245a02da53cac9"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">In BlazorTrain <a href="https://youtu.be/VaX73p3JfV4" target="_blank">episode 16</a> I show off a nice set of interfaces and managers for implementing a complete data access solution. The controllers use a generic EF Core data manager to access data given an EF entity and dbContext. You can set up an API layer in minutes, rather than hours or days. For more episodes check out <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/2/simplifying-data-access-in-blazor" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f7788172d0ee66430ed4721"></a> <span class="sqs-simple-like" data-item-id="5f7788172d0ee66430ed4721" data-like-count="1"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f7788172d0ee66430ed4721/1601669592802/" data-record-type="1" data-full-url="/blog/2020/10/2/simplifying-data-access-in-blazor" data-title="Simplifying Data Access in Blazor"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-8" id="article-5f7785ccae32bb7d3fda2e62" data-item-id="5f7785ccae32bb7d3fda2e62"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/application-state-in-blazor" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/2/application-state-in-blazor" class="u-url" rel="bookmark">Application State in Blazor</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/application-state-in-blazor" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1601668988784" id="item-5f7785ccae32bb7d3fda2e62"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1601664367065_48335"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="17-scereen-shot.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601668957497-K7M2JZID6QF9LOMECZMF/17-scereen-shot.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-164ba3868bd4d1ecf2bd"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">I’ve developed a couple patterns for managing and storing application state in a Blazor application. I described these in BlazorTrain <a href="https://youtu.be/BB4lK2kfKf0" target="_blank">episode 15</a> and <a href="https://youtu.be/ib_6mYbkL2s" target="_blank">episode 17</a> . More free episodes at <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a></p><p class="" data-rte-preserve-empty="true" style="white-space:pre-wrap;"></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/2/application-state-in-blazor" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f7785ccae32bb7d3fda2e62"></a> <span class="sqs-simple-like" data-item-id="5f7785ccae32bb7d3fda2e62" data-like-count="1"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f7785ccae32bb7d3fda2e62/1601668987120/" data-record-type="1" data-full-url="/blog/2020/10/2/application-state-in-blazor" data-title="Application State in Blazor"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-9 featured" id="article-5f77757d71a9992d995d9d44" data-item-id="5f77757d71a9992d995d9d44"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" class="u-url" rel="bookmark">Add Blazor to an Existing MVC Application</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-10-02" pubdate><a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" class="entry-dateline-link">October 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1601664955249" id="item-5f77757d71a9992d995d9d44"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1601664367065_14964"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1200px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:66.66667175292969%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg" data-image-dimensions="1200x800" data-image-focal-point="0.5,0.5" alt="programmer.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg" width="1200" height="800" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1601664571619-MCUNTZ0PBEDTL6U4FFQF/programmer.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-d6fd6f46fabc767d6385"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">So you want to add some Blazor pages to your ASP.NET Core MVC application? <a href="https://www.youtube.com/watch?feature=youtu.be&v=rfPQytn7tfk" target="_blank">This 12-minute video</a> shows you exactly how in only seven steps. You can check out Brian MacKay’s Github repo at <a href="https://github.com/kinetiq/Blazor.WithMVC/" target="_blank">https://github.com/kinetiq/Blazor.WithMVC/</a></p><p class="" style="white-space:pre-wrap;">Want more BlazorTrain? Go to <a href="https://blazortrain.com" target="_blank">https://blazortrain.com</a> for a complete list of episodes.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" class="sqs-comment-link sqs-disqus-comment-link" data-id="5f77757d71a9992d995d9d44"></a> <span class="sqs-simple-like" data-item-id="5f77757d71a9992d995d9d44" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5f77757d71a9992d995d9d44/1601670631846/" data-record-type="1" data-full-url="/blog/2020/10/2/add-blazor-to-an-existing-mvc-application" data-title="Add Blazor to an Existing MVC Application"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-10" id="article-5e9d65e20feebf4d48711756" data-item-id="5e9d65e20feebf4d48711756"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-04-20" pubdate><a href="/blog/2020/4/20/playing-audio-files-in-a-blazor-application" class="entry-dateline-link">April 20, 2020</a></time> <time class="dt-updated updated" datetime="2020-04-20"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/4/20/playing-audio-files-in-a-blazor-application" class="u-url" rel="bookmark">Playing Audio Files in a Blazor Application</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-04-20" pubdate><a href="/blog/2020/4/20/playing-audio-files-in-a-blazor-application" class="entry-dateline-link">April 20, 2020</a></time> <time class="dt-updated updated" datetime="2020-04-20"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1587373598128" id="item-5e9d65e20feebf4d48711756"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-b5c2f2a603dce705696f"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">With a little JavaScript Interop you can play mp3 files in your Blazor application.</h2><p class="" style="white-space:pre-wrap;">The first step is to gather a few mp3 files you can play in your app. Remember, these will be playing in the browser, so keep the size of the file in mind. You can <a href="http://pwop.com/download/sounds.zip">download my demo audio files here</a>.</p><p class="" style="white-space:pre-wrap;">Next, create a new Blazor app with <a href="https://visualstudio.microsoft.com/downloads/" target="_blank">Visual Studio 2019</a>. You can use the free Community Edition if you like.</p><p class="" style="white-space:pre-wrap;">Next, create a <em>sounds</em> folder under the <em>wwwroot</em> folder, and copy your mp3 files to it. For each file, make sure the <strong>Build Action</strong> property is set to <strong>Content</strong>, and the <strong>Copy to Output Directory</strong> property is set to <strong>Copy if newer</strong>.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1587373522777_33251"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:323px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:253.5603790283203%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png" data-image-dimensions="323x819" data-image-focal-point="0.5,0.5" alt="audio_demo_shot1.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png" width="323" height="819" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1587373800132-MDRRCW5LSC2HDZLKZ0NU/audio_demo_shot1.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587373868813_4953"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">For our demo, we’re going to hijack the <em>Index.razor</em> file, replacing it with the following:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587373868813_5564"><div class="sqs-block-content"><pre class="source-code">@page "/" @inject IJSRuntime js; <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span>Play MP3 Files<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile1"</span><span class="cm-tag cm-bracket">></span>Duh<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span><span class="cm-atom">&nbsp;</span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile2"</span><span class="cm-tag cm-bracket">></span>Dwayne<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">audio</span> <span class="cm-attribute">id</span>=<span class="cm-string">"player"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">source</span> <span class="cm-attribute">id</span>=<span class="cm-string">"playerSource"</span> <span class="cm-attribute">src</span>=<span class="cm-string">""</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">audio</span><span class="cm-tag cm-bracket">></span> @code { async Task PlayAudioFile1() { await js.InvokeVoidAsync("PlayAudioFile", "/sounds/duh.mp3"); } async Task PlayAudioFile2() { await js.InvokeVoidAsync("PlayAudioFile", "/sounds/dwayne.mp3"); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587373868813_6928"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">All we’ve done here is set up a couple buttons to execute code that calls into a JavaScript function called "PlayAudioFile”, which we have yet to write. Let’s do that now.</p><p class="" style="white-space:pre-wrap;">Open your startup HTML file. In Blazor Server this is <em>Pages/_Host.cshtml</em> and for Blazor WebAssembly it’s <em>wwwroot/Index.html</em>.</p><p class="" style="white-space:pre-wrap;">Add the following to it, just below the script tag that loads the blazor JavaScript file:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587373868813_8174"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">script</span><span class="cm-tag cm-bracket">></span> <span class="cm-variable">window</span>.<span class="cm-property">PlayAudioFile</span> <span class="cm-operator">=</span> (<span class="cm-def">src</span>) <span class="cm-operator">=></span> { <span class="cm-keyword">var</span> <span class="cm-def">audio</span> <span class="cm-operator">=</span> <span class="cm-variable">document</span>.<span class="cm-property">getElementById</span>(<span class="cm-string">'player'</span>); <span class="cm-keyword">if</span> (<span class="cm-variable-2">audio</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) { <span class="cm-keyword">var</span> <span class="cm-def">audioSource</span> <span class="cm-operator">=</span> <span class="cm-variable">document</span>.<span class="cm-property">getElementById</span>(<span class="cm-string">'playerSource'</span>); <span class="cm-keyword">if</span> (<span class="cm-variable-2">audioSource</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) { <span class="cm-variable-2">audioSource</span>.<span class="cm-property">src</span> <span class="cm-operator">=</span> <span class="cm-variable-2">src</span>; <span class="cm-variable-2">audio</span>.<span class="cm-property">load</span>(); <span class="cm-variable-2">audio</span>.<span class="cm-property">play</span>(); } } } <span class="cm-tag cm-bracket"></</span><span class="cm-tag">script</span><span class="cm-tag cm-bracket">></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587373868813_8926"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">This code finds your audio player, sets the source file, loads it, and plays it.</p><p class="" style="white-space:pre-wrap;">Go ahead and try it.</p><h3 style="white-space:pre-wrap;">Play Multiple Audio Files Simultaneously</h3><p class="" style="white-space:pre-wrap;">You’ll notice that if you click the first button, and then the second button immediately, the second mp3 file cuts off the first one. You can fix this in a rather inelegant, yet functional, way. </p><p class="" style="white-space:pre-wrap;">Create another audio tag for the second mp3 so you can play two simultaneously. More files? Just add more tags. You’ll have to modify your code to handle the change.</p><p class="" style="white-space:pre-wrap;">Change the contents of <em>Index.razor</em> to this:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587373522777_67603"><div class="sqs-block-content"><pre class="source-code">@page "/" @inject IJSRuntime js; <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span>Play MP3 Files<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile1"</span><span class="cm-tag cm-bracket">></span>Duh<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span><span class="cm-atom">&nbsp;</span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile2"</span><span class="cm-tag cm-bracket">></span>Dwayne<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">audio</span> <span class="cm-attribute">id</span>=<span class="cm-string">"player1"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">source</span> <span class="cm-attribute">id</span>=<span class="cm-string">"playerSource1"</span> <span class="cm-attribute">src</span>=<span class="cm-string">""</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">audio</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">audio</span> <span class="cm-attribute">id</span>=<span class="cm-string">"player2"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">source</span> <span class="cm-attribute">id</span>=<span class="cm-string">"playerSource2"</span> <span class="cm-attribute">src</span>=<span class="cm-string">""</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">audio</span><span class="cm-tag cm-bracket">></span> @code { async Task PlayAudioFile1() { await js.InvokeVoidAsync("PlayAudioFile", "1", "/sounds/duh.mp3"); } async Task PlayAudioFile2() { await js.InvokeVoidAsync("PlayAudioFile", "2", "/sounds/dwayne.mp3"); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587373522777_68868"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">We’ve added a second audio tag, and named them “player1” and “player2”. We’ve also updated the names of the source elements to “playerSource1” and “playerSource2”.</p><p class="" style="white-space:pre-wrap;">We’ve also modified our JavaScript calls to pass the player number as a string.</p><p class="" style="white-space:pre-wrap;">Modify your PlayAudioFile JavaScript function to handle the change:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587373522777_70107"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">script</span><span class="cm-tag cm-bracket">></span> <span class="cm-variable">window</span>.<span class="cm-property">PlayAudioFile</span> <span class="cm-operator">=</span> (<span class="cm-def">playerNumber</span>, <span class="cm-def">src</span>) <span class="cm-operator">=></span> { <span class="cm-keyword">var</span> <span class="cm-def">audio</span> <span class="cm-operator">=</span> <span class="cm-variable">document</span>.<span class="cm-property">getElementById</span>(<span class="cm-string">'player'</span> <span class="cm-operator">+</span> <span class="cm-variable-2">playerNumber</span>); <span class="cm-keyword">if</span> (<span class="cm-variable-2">audio</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) { <span class="cm-keyword">var</span> <span class="cm-def">audioSource</span> <span class="cm-operator">=</span> <span class="cm-variable">document</span>.<span class="cm-property">getElementById</span>(<span class="cm-string">'playerSource'</span> <span class="cm-operator">+</span> <span class="cm-variable-2">playerNumber</span>); <span class="cm-keyword">if</span> (<span class="cm-variable-2">audioSource</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) { <span class="cm-variable-2">audioSource</span>.<span class="cm-property">src</span> <span class="cm-operator">=</span> <span class="cm-variable-2">src</span>; <span class="cm-variable-2">audio</span>.<span class="cm-property">load</span>(); <span class="cm-variable-2">audio</span>.<span class="cm-property">play</span>(); } } } <span class="cm-tag cm-bracket"></</span><span class="cm-tag">script</span><span class="cm-tag cm-bracket">></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587373522777_71374"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Now you have two “channels” with which to play audio files. Great. </p><h2 style="white-space:pre-wrap;">Load and play audio dynamically!</h2><p class="" style="white-space:pre-wrap;">Wouldn’t it be great if we didn’t have to provision these audio controls before using them? With a little more JavaScript we can create them on the fly, load the audio file, and play them. Now the audio files will play completely asynchronously.</p><p class="" style="white-space:pre-wrap;">Change your <em>Index.razor</em> contents back to the first version minus the audio element:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587382415500_8894"><div class="sqs-block-content"><pre class="source-code">@page "/" @inject IJSRuntime js; <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span>Play MP3 Files<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h1</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile1"</span><span class="cm-tag cm-bracket">></span>Duh<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span><span class="cm-atom">&nbsp;</span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">span</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">class</span>=<span class="cm-string">"btn btn-primary"</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"PlayAudioFile2"</span><span class="cm-tag cm-bracket">></span>Dwayne<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> @code { async Task PlayAudioFile1() { await js.InvokeVoidAsync("PlayAudioFile", "/sounds/duh.mp3"); } async Task PlayAudioFile2() { await js.InvokeVoidAsync("PlayAudioFile", "/sounds/dwayne.mp3"); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587382415500_9842"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Now, let’s change the JavaScript function to create the audio tag, set the properties, add it to the document, load the audio, and play.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1587382415500_10410"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">window</span>.<span class="cm-property">PlayAudioFile</span> <span class="cm-operator">=</span> (<span class="cm-def">src</span>) <span class="cm-operator">=></span> { <span class="cm-keyword">var</span> <span class="cm-def">sound</span> <span class="cm-operator">=</span> <span class="cm-variable">document</span>.<span class="cm-property">createElement</span>(<span class="cm-string">'audio'</span>); <span class="cm-variable-2">sound</span>.<span class="cm-property">src</span> <span class="cm-operator">=</span> <span class="cm-variable-2">src</span>; <span class="cm-variable-2">sound</span>.<span class="cm-property">type</span> <span class="cm-operator">=</span> <span class="cm-string">'audio/mpeg'</span>; <span class="cm-variable">document</span>.<span class="cm-property">body</span>.<span class="cm-property">appendChild</span>(<span class="cm-variable-2">sound</span>); <span class="cm-variable-2">sound</span>.<span class="cm-property">load</span>(); <span class="cm-variable-2">sound</span>.<span class="cm-property">play</span>(); }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1587382415500_11061"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Enjoy!</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/4/20/playing-audio-files-in-a-blazor-application" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e9d65e20feebf4d48711756"></a> <span class="sqs-simple-like" data-item-id="5e9d65e20feebf4d48711756" data-like-count="3"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5e9d65e20feebf4d48711756/1587382688880/" data-record-type="1" data-full-url="/blog/2020/4/20/playing-audio-files-in-a-blazor-application" data-title="Playing Audio Files in a Blazor Application"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-11" id="article-5e7e44a0971cba01a83ede40" data-item-id="5e7e44a0971cba01a83ede40"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-03-27" pubdate><a href="/blog/2020/3/27/blazor-webassembly-320-preview-3-release-now-available" class="entry-dateline-link">March 27, 2020</a></time> <time class="dt-updated updated" datetime="2020-03-27"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/3/27/blazor-webassembly-320-preview-3-release-now-available" class="u-url" rel="bookmark">Blazor WebAssembly 3.2.0 Preview 3 release now available</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-03-27" pubdate><a href="/blog/2020/3/27/blazor-webassembly-320-preview-3-release-now-available" class="entry-dateline-link">March 27, 2020</a></time> <time class="dt-updated updated" datetime="2020-03-27"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1585333584117" id="item-5e7e44a0971cba01a83ede40"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-7c7512713e9b52165a4f"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Yesterday, Microsoft released a <a href="https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-3-release-now-available/" target="_blank">new preview of Blazor WebAssembl</a>y. This is very exciting because we get a first look at debugging WebAssembly Blazor apps in Visual Studio. Other features include Auto-rebuild in Visual Studio, Accessing config files, and a new HttpClient extension methods for JSON handling.</p><p class="" style="white-space:pre-wrap;">I am downloading it now, and hope you are as well!</p><p class="" data-rte-preserve-empty="true" style="white-space:pre-wrap;"></p><p class="" data-rte-preserve-empty="true" style="white-space:pre-wrap;"></p><p class="" data-rte-preserve-empty="true" style="white-space:pre-wrap;"></p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/3/27/blazor-webassembly-320-preview-3-release-now-available" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e7e44a0971cba01a83ede40"></a> <span class="sqs-simple-like" data-item-id="5e7e44a0971cba01a83ede40" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5e7e44a0971cba01a83ede40/1585336566476/" data-record-type="1" data-full-url="/blog/2020/3/27/blazor-webassembly-320-preview-3-release-now-available" data-title="Blazor WebAssembly 3.2.0 Preview 3 release now available"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-12" id="article-5e646fd59369fa095302690e" data-item-id="5e646fd59369fa095302690e"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-03-10" pubdate><a href="/blog/2020/3/7/blazor-shopping-cart-sample-using-local-storage-to-persist-state" class="entry-dateline-link">March 10, 2020</a></time> <time class="dt-updated updated" datetime="2020-03-10"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/3/7/blazor-shopping-cart-sample-using-local-storage-to-persist-state" class="u-url" rel="bookmark">Blazor Shopping Cart Sample using Local Storage to Persist State</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-03-10" pubdate><a href="/blog/2020/3/7/blazor-shopping-cart-sample-using-local-storage-to-persist-state" class="entry-dateline-link">March 10, 2020</a></time> <time class="dt-updated updated" datetime="2020-03-10"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1583640534037" id="item-5e646fd59369fa095302690e"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-17e571c7c57875f19c4e"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">This little demo is based on a <a href="https://gist.github.com/SteveSandersonMS/ba16f6bb6934842d78c89ab5314f4b56" target="_blank">Gist written by Steve Sanderson</a> where he shows how to persist state in the browser using sessionStorage. His demo shows how to persist the counter value but I wanted to try something a bit more real-world, a very simple shopping cart, and also to use <a href="https://www.w3schools.com/html/html5_webstorage.asp" target="_blank">localStorage</a> rather than sessionStorage. <a href="https://drive.google.com/file/d/1olEdDc6F5JG6gnbWSW6jn_Zm23br9f6j/view?usp=sharing" target="_blank">You can download the project here</a>.</p><p class="" style="white-space:pre-wrap;">Here’s how it works. Select an item from the store (in this case a list). The details of that item are shown below the list. Enter a quantity and add it to your cart. The contents of your cart are shown below that, including the grand total. Now refresh the browser (or close and re-open). If you refresh within 30 seconds, your cart is still there. If you wait longer than 30 seconds before refreshing the browser, the cart data is no longer there.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1583860095592_15006"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1024px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:62.3046875%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif" data-image-dimensions="1024x638" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif" width="1024" height="638" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1583860415129-5AOS8CF5V95UM3CJNDAC/image-asset.gif?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_19056"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">My top level class is called Cart. Every time you update the cart, the contents are serialized and stored in the browser’s local storage using a not-as-yet-released package, Microsoft.AspNetCore.ProtectedBrowserStorage. </p><p class="" style="white-space:pre-wrap;">I have added a couple fields to the Cart class to support expiration:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_41957"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">public</span> <span class="cm-variable">DateTime</span> <span class="cm-variable">LastAccessed</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">int</span> <span class="cm-variable">TimeToLiveInSeconds</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-operator">=</span> <span class="cm-number">30</span>; <span class="cm-comment">// default</span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_43208"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Whenever the Cart is persisted to localStorage, the LastAccessed time stamp is saved. When loading the cart from localStorage, a check is made to see if the cart has expired based on the LastAccessed and TimeToLiveInSeconds properties. I set it to 30 seconds by default so you could easily test it without having to wait around for a long period of time to elapse.</p><h2 style="white-space:pre-wrap;">The Code</h2><p class="" style="white-space:pre-wrap;">I created a Blazor Server project in Visual Studio 2019 called BlazorStateTest1.</p><p class="" style="white-space:pre-wrap;">Let’s start with the three models:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_79558"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">public</span> <span class="cm-keyword">class</span> <span class="cm-def">Product</span> { <span class="cm-property">public</span> <span class="cm-def">int</span> <span class="cm-def">Id</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">Name</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">Description</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">decimal</span> <span class="cm-variable">Price</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } } <span class="cm-variable">public</span> <span class="cm-keyword">class</span> <span class="cm-def">CartProduct</span> { <span class="cm-property">public</span> <span class="cm-def">int</span> <span class="cm-def">Quantity</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">Product</span> <span class="cm-variable">Product</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">decimal</span> <span class="cm-variable">Total</span> { <span class="cm-variable">get</span> { <span class="cm-keyword">return</span> <span class="cm-variable">Product</span>.<span class="cm-property">Price</span> <span class="cm-operator">*</span> <span class="cm-variable">Quantity</span>; } } } <span class="cm-variable">public</span> <span class="cm-keyword">class</span> <span class="cm-def">Cart</span> { <span class="cm-property">public</span> <span class="cm-def">List</span><span class="cm-operator"><</span><span class="cm-variable">CartProduct</span><span class="cm-operator">></span> <span class="cm-variable">Items</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">List</span><span class="cm-operator"><</span><span class="cm-variable">CartProduct</span><span class="cm-operator">></span>(); <span class="cm-variable">public</span> <span class="cm-variable">Decimal</span> <span class="cm-variable">Total</span> { <span class="cm-variable">get</span> { <span class="cm-variable">decimal</span> <span class="cm-variable">total</span> <span class="cm-operator">=</span> (<span class="cm-variable">decimal</span>)<span class="cm-number">0.0</span>; <span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">item</span> <span class="cm-keyword">in</span> <span class="cm-variable">Items</span>) { <span class="cm-variable">total</span> <span class="cm-operator">+=</span> <span class="cm-variable">item</span>.<span class="cm-property">Total</span>; } <span class="cm-keyword">return</span> <span class="cm-variable">total</span>; } } <span class="cm-variable">public</span> <span class="cm-variable">DateTime</span> <span class="cm-variable">LastAccessed</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">int</span> <span class="cm-variable">TimeToLiveInSeconds</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-operator">=</span> <span class="cm-number">30</span>; <span class="cm-comment">// default</span> } </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_81464"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">At the very bottom of the food chain is Product. Simple enough. Then we have a CartProduct which represents a Product and Quantity, and has a Total property that multiplies the product price by the quantity.</p><p class="" style="white-space:pre-wrap;">Finally, the Cart object has a list of CartProducts and the aforementioned properties to support expiration. This is a greatly simplified shopping cart. The focus here should be on the persistence patterns. </p><p class="" style="white-space:pre-wrap;">Following Steve’s instruction, I added the Microsoft.AspNetCore.ProtectedBrowserStorage package to the project.</p><p class="" style="white-space:pre-wrap;">Next, I added this line to my ConfigureServices method in Startup.cs:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_108888"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">services</span>.<span class="cm-property">AddProtectedBrowserStorage</span>();</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_110059"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, I created a component called CartStateProvider.razor to handle loading and saving the cart:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_110757"><div class="sqs-block-content"><pre class="source-code">@<span class="cm-variable">inject</span> <span class="cm-variable">ProtectedLocalStorage</span> <span class="cm-variable">ProtectedLocalStore</span> @<span class="cm-keyword">if</span> (<span class="cm-variable">hasLoaded</span>) { <span class="cm-operator"><</span><span class="cm-variable">CascadingValue</span> <span class="cm-variable">Value</span><span class="cm-operator">=</span><span class="cm-string">"@this"</span><span class="cm-operator">></span> @<span class="cm-variable">ChildContent</span> <span class="cm-operator"><</span><span class="cm-string-2">/CascadingValue></span> } <span class="cm-keyword">else</span> { <span class="cm-operator"><</span><span class="cm-variable">p</span><span class="cm-operator">></span><span class="cm-variable">Loading</span><span class="cm-meta">...</span><span class="cm-operator"><</span><span class="cm-string-2">/p></span> } @<span class="cm-variable">code</span> { [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">RenderFragment</span> <span class="cm-variable">ChildContent</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">public</span> <span class="cm-variable">Cart</span> <span class="cm-variable">ShoppingCart</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">bool</span> <span class="cm-variable">hasLoaded</span>; <span class="cm-variable">protected</span> <span class="cm-variable">override</span> <span class="cm-keyword">async</span> <span class="cm-variable">Task</span> <span class="cm-variable">OnParametersSetAsync</span>() { <span class="cm-comment">// Retrieve the Shopping Cart from Local Storage (in the browser)</span> <span class="cm-variable">ShoppingCart</span> <span class="cm-operator">=</span> <span class="cm-keyword">await</span> <span class="cm-variable">ProtectedLocalStore</span>.<span class="cm-property">GetAsync</span><span class="cm-operator"><</span><span class="cm-variable">Cart</span><span class="cm-operator">></span>(<span class="cm-string">"MyShoppingCart"</span>); <span class="cm-comment">// If the Cart is not there or empty...</span> <span class="cm-keyword">if</span> (<span class="cm-variable">ShoppingCart</span> <span class="cm-operator">==</span> <span class="cm-atom">null</span> <span class="cm-operator">||</span> <span class="cm-variable">ShoppingCart</span>.<span class="cm-property">Items</span>.<span class="cm-property">Count</span> <span class="cm-operator">==</span> <span class="cm-number">0</span>) { <span class="cm-comment">// Create a new Cart</span> <span class="cm-variable">ShoppingCart</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Cart</span>(); } <span class="cm-keyword">else</span> { <span class="cm-comment">// otherwise, check to see if the Cart has expired (default is 30 seconds)</span> <span class="cm-keyword">if</span> (<span class="cm-variable">DateTime</span>.<span class="cm-property">Now</span> <span class="cm-operator">></span> <span class="cm-variable">ShoppingCart</span>.<span class="cm-property">LastAccessed</span>.<span class="cm-property">AddSeconds</span>(<span class="cm-variable">ShoppingCart</span>.<span class="cm-property">TimeToLiveInSeconds</span>)) { <span class="cm-comment">// Expired. Create a new cart.</span> <span class="cm-variable">ShoppingCart</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Cart</span>(); } } <span class="cm-variable">ShoppingCart</span>.<span class="cm-property">LastAccessed</span> <span class="cm-operator">=</span> <span class="cm-variable">DateTime</span>.<span class="cm-property">Now</span>; <span class="cm-variable">hasLoaded</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>; } <span class="cm-variable">public</span> <span class="cm-keyword">async</span> <span class="cm-variable">Task</span> <span class="cm-variable">SaveChangesAsync</span>() { <span class="cm-comment">// Set the time stamp to current time and save to local storage (in the browser).</span> <span class="cm-variable">ShoppingCart</span>.<span class="cm-property">LastAccessed</span> <span class="cm-operator">=</span> <span class="cm-variable">DateTime</span>.<span class="cm-property">Now</span>; <span class="cm-keyword">await</span> <span class="cm-variable">ProtectedLocalStore</span>.<span class="cm-property">SetAsync</span>(<span class="cm-string">"MyShoppingCart"</span>, <span class="cm-variable">ShoppingCart</span>); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_127576"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">This component has an instance of Cart (ShoppingCart) which it loads and saves to localStorage. </p><p class="" style="white-space:pre-wrap;">Rather than just instantiating a CartStateProvider in index, I took Steve’s advice and made it a Cascading Parameter so it could be used everywhere. To do that, I wrapped the Router in App.razor in an instance of CartStateProvider:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_128742"><div class="sqs-block-content"><pre class="source-code"><span class="cm-tag cm-bracket"><</span><span class="cm-tag">BlazorStateTest1.Shared.CartStateProvider</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">Router</span> <span class="cm-attribute">AppAssembly</span>=<span class="cm-string">"@typeof(Program).Assembly"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">Found</span> <span class="cm-attribute">Context</span>=<span class="cm-string">"routeData"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">RouteView</span> <span class="cm-attribute">RouteData</span>=<span class="cm-string">"@routeData"</span> <span class="cm-attribute">DefaultLayout</span>=<span class="cm-string">"@typeof(MainLayout)"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">Found</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">NotFound</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">LayoutView</span> <span class="cm-attribute">Layout</span>=<span class="cm-string">"@typeof(MainLayout)"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">p</span><span class="cm-tag cm-bracket">></span>Sorry, there's nothing at this address.<span class="cm-tag cm-bracket"></</span><span class="cm-tag">p</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">LayoutView</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">NotFound</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">Router</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">BlazorStateTest1.Shared.CartStateProvider</span><span class="cm-tag cm-bracket">></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_145865"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Finally, we can use it in the Index page (the default page for Blazor projects):</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_146399"><div class="sqs-block-content"><pre class="source-code">@page "/" @if (AllProducts != null) { // This demo is based on a technique Steve Sanderson Showed in the following Github Gist: // https://gist.github.com/SteveSandersonMS/ba16f6bb6934842d78c89ab5314f4b56 <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h2</span><span class="cm-tag cm-bracket">></span>Select an item<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h2</span><span class="cm-tag cm-bracket">></span> //Display the list of products. Call ProductSelected when one is selected <span class="cm-tag cm-bracket"><</span><span class="cm-tag">select</span> <span class="cm-attribute">size</span>=<span class="cm-string">"4"</span> <span class="cm-attribute">style</span>=<span class="cm-string">"width:100%;"</span> <span class="cm-attribute">@onchange</span>=<span class="cm-string">"ProductSelected"</span><span class="cm-tag cm-bracket">></span> @foreach (var product in AllProducts) { <span class="cm-tag cm-bracket"><</span><span class="cm-tag">option</span> <span class="cm-attribute">value</span>=<span class="cm-string">"@product.Id.ToString()"</span><span class="cm-tag cm-bracket">></span>@product.Name<span class="cm-tag cm-bracket"></</span><span class="cm-tag">option</span><span class="cm-tag cm-bracket">></span> } <span class="cm-tag cm-bracket"></</span><span class="cm-tag">select</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> // Show the selected product @if (SelectedProduct != null <span class="cm-error">&</span><span class="cm-error">&</span> ShowItem == true) { <span class="cm-tag cm-bracket"><</span><span class="cm-tag">div</span> <span class="cm-attribute">style</span>=<span class="cm-string">"padding:1vw;background-color:lightgray;"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">table</span> <span class="cm-attribute">cellpadding</span>=<span class="cm-string">"5"</span> <span class="cm-attribute">cellspacing</span>=<span class="cm-string">"5"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Name:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>@SelectedProduct.Name<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Description:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>@SelectedProduct.Description<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Price:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>$@SelectedProduct.Price<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Add To Cart:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span> Quantity: <span class="cm-tag cm-bracket"><</span><span class="cm-tag">input</span> <span class="cm-attribute">@bind</span>=<span class="cm-string">"Quantity"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"AddToCart"</span><span class="cm-tag cm-bracket">></span>Add<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">table</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">div</span><span class="cm-tag cm-bracket">></span> } // Show the cart contents if there are items in it. @if (CartStateProvider != null <span class="cm-error">&</span><span class="cm-error">&</span> CartStateProvider.ShoppingCart.Items.Count > 0) { <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h3</span><span class="cm-tag cm-bracket">></span>Your Cart:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h3</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h4</span><span class="cm-tag cm-bracket">></span>Total: $@CartStateProvider.ShoppingCart.Total<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h4</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">table</span> <span class="cm-attribute">cellpadding</span>=<span class="cm-string">"5"</span> <span class="cm-attribute">cellspacing</span>=<span class="cm-string">"5"</span><span class="cm-tag cm-bracket">></span> @foreach (var item in CartStateProvider.ShoppingCart.Items) { <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">colspan</span>=<span class="cm-string">"2"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">hr</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Name:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>@item.Product.Name<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Description:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>@item.Product.Description<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Price:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>$@item.Product.Price<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Quantity:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>@item.Quantity<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"right"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span>Total:<span class="cm-tag cm-bracket"></</span><span class="cm-tag">strong</span><span class="cm-tag cm-bracket">></span><span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">align</span>=<span class="cm-string">"left"</span> <span class="cm-attribute">valign</span>=<span class="cm-string">"top"</span><span class="cm-tag cm-bracket">></span>$@item.Total<span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">td</span> <span class="cm-attribute">colspan</span>=<span class="cm-string">"2"</span><span class="cm-tag cm-bracket">></span> @*Clicking this button passes the item so we can remove it*@ <span class="cm-tag cm-bracket"><</span><span class="cm-tag">button</span> <span class="cm-attribute">@onclick</span>=<span class="cm-string">"@(() => RemoveItem(@item))"</span><span class="cm-tag cm-bracket">></span>Remove<span class="cm-tag cm-bracket"></</span><span class="cm-tag">button</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">td</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">tr</span><span class="cm-tag cm-bracket">></span> } <span class="cm-tag cm-bracket"></</span><span class="cm-tag">table</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">h4</span><span class="cm-tag cm-bracket">></span>Total: $@CartStateProvider.ShoppingCart.Total<span class="cm-tag cm-bracket"></</span><span class="cm-tag">h4</span><span class="cm-tag cm-bracket">></span> } } @code { // Cascading Parameters and Values flow down the entire component tree [CascadingParameter] CartStateProvider CartStateProvider { get; set; } bool ShowItem = false; string Quantity = "1"; List<span class="cm-tag cm-bracket"><</span><span class="cm-tag">Product</span><span class="cm-tag cm-bracket">></span> AllProducts; Product SelectedProduct; void ProductSelected(ChangeEventArgs args) { // User clicked on an item in the list. // Show the product and give them an option to add to cart. SelectedProduct = (from x in AllProducts where x.Id == Convert.ToInt32(args.Value) select x).First(); Quantity = "1"; ShowItem = true; } async Task AddToCart() { // Create a new item for the shopping cart var item = new CartProduct { Product = SelectedProduct, Quantity = Convert.ToInt32(Quantity) }; // Add it to the cart CartStateProvider.ShoppingCart.Items.Add(item); // Save to local storage await CartStateProvider.SaveChangesAsync(); // Stop displaying the selected item ShowItem = false; } async Task RemoveItem(CartProduct Item) { // User clicked a Remove button to remove an item from the cart. CartStateProvider.ShoppingCart.Items.Remove(Item); // Update the cart - save to localstorage await CartStateProvider.SaveChangesAsync(); } protected override void OnInitialized() { // Create the products we can purchase AllProducts = new List<span class="cm-tag cm-bracket"><</span><span class="cm-tag">Product</span><span class="cm-tag cm-bracket">></span>(); AllProducts.Add(new Product { Id = 1, Name = "1 lb. Bag of Yirgacheffe Coffee Beans", Description = "Yirgacheffe is a rich Ethiopian coffee that will poke your eye out", Price = (decimal)10.99 }); AllProducts.Add(new Product { Id = 2, Name = "Tablet Show Coffee Mug", Description = "Back by popular demand for a limited time, the long-coveted Tablet Show Coffee Mug", Price = (decimal)4.99 }); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_148433"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Everything starts at the OnInitialized method. That’s where I create two products and add them to the AllProducts list.</p><p class="" style="white-space:pre-wrap;">Now look at the top of the page. If the AllProducts list is loaded, we show the products in a select element.</p><p class="" style="white-space:pre-wrap;">When an item is selected, the ProductSelected method fires, and the SelectedProduct is set.</p><p class="" style="white-space:pre-wrap;">Next, we have logic that shows the product info if SelectedProduct isn’t null and the boolean ShowItem is set to true. That table includes an input element for the quantity and an Add button that calls the AddToCart method when clicked.</p><p class="" style="white-space:pre-wrap;">The AddToCart method then creates a CartProduct, adds it to the CartStateProvider.ShoppingCart, and persists it.</p><p class="" style="white-space:pre-wrap;">Next, if the CartStateProvider.ShoppingCart isn’t null and actually contains items, it is shown below, again in a table element. Each item has a Remove button that when clicked calls RemoveItem, which removes the item from the cart and again saves the cart.</p><p class="" style="white-space:pre-wrap;">Take a look at the expiry code in the CartStateProvider’s OnParametersSetAsync method. This happens when the page loads and the cart gets instantiated. This is the critical check right here:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1583860095592_181158"><div class="sqs-block-content"><pre class="source-code"><span class="cm-keyword">if</span> (<span class="cm-variable">DateTime</span>.<span class="cm-property">Now</span> <span class="cm-operator">></span> <span class="cm-variable">ShoppingCart</span>.<span class="cm-property">LastAccessed</span>.<span class="cm-property">AddSeconds</span>(<span class="cm-variable">ShoppingCart</span>.<span class="cm-property">TimeToLiveInSeconds</span>)) </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1583860095592_184458"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">If this statement is true, then the cart has expired and a new empty cart is returned.</p><h2 style="white-space:pre-wrap;">Summary</h2><p class="" style="white-space:pre-wrap;">LocalStorage is good for small amounts of data - Steve says storing a few K of data there is probably okay - but you should be thinking about server-side storage of state if the amount of data going over the wire is hindering performance. For small bits, however, localStorage is the perfect solution.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/3/7/blazor-shopping-cart-sample-using-local-storage-to-persist-state" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e646fd59369fa095302690e"></a> <span class="sqs-simple-like" data-item-id="5e646fd59369fa095302690e" data-like-count="2"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5e646fd59369fa095302690e/1583865695303/" data-record-type="1" data-full-url="/blog/2020/3/7/blazor-shopping-cart-sample-using-local-storage-to-persist-state" data-title="Blazor Shopping Cart Sample using Local Storage to Persist State"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-13" id="article-5e558acadf34243c4af60567" data-item-id="5e558acadf34243c4af60567"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-25" pubdate><a href="/blog/2020/2/25/blazor-workshop-suspended-until-after-blazor-client-is-baked-in-may-2020" class="entry-dateline-link">February 25, 2020</a></time> <time class="dt-updated updated" datetime="2020-02-25"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/2/25/blazor-workshop-suspended-until-after-blazor-client-is-baked-in-may-2020" class="u-url" rel="bookmark">Blazor Workshop Suspended until after Blazor Client is baked in May, 2020</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-25" pubdate><a href="/blog/2020/2/25/blazor-workshop-suspended-until-after-blazor-client-is-baked-in-may-2020" class="entry-dateline-link">February 25, 2020</a></time> <time class="dt-updated updated" datetime="2020-02-25"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1582664595242" id="item-5e558acadf34243c4af60567"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-ca4b6dfd320e85c0e3c6"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">I have decided to cancel the <a href="http://blazor0413.appvnext.com/" target="_blank">April Online Blazor Workshop</a> because Blazor Client (WebAssembly) hasn’t settled yet, and the Preview code is affecting other packages I use. It’s still a moving target. Meanwhile come see me on my <a href="http://blazorroadshow.com">Blazor Road Show</a> to explore the possibilities that Blazor brings us.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/2/25/blazor-workshop-suspended-until-after-blazor-client-is-baked-in-may-2020" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e558acadf34243c4af60567"></a> <span class="sqs-simple-like" data-item-id="5e558acadf34243c4af60567" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5e558acadf34243c4af60567/1582665160048/" data-record-type="1" data-full-url="/blog/2020/2/25/blazor-workshop-suspended-until-after-blazor-client-is-baked-in-may-2020" data-title="Blazor Workshop Suspended until after Blazor Client is baked in May, 2020"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-14" id="article-5e4bf872fd3ee771bded2bfc" data-item-id="5e4bf872fd3ee771bded2bfc"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-18" pubdate><a href="/blog/2020/2/18/is-blazor-for-everyone" class="entry-dateline-link">February 18, 2020</a></time> <time class="dt-updated updated" datetime="2020-02-18"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/2/18/is-blazor-for-everyone" class="u-url" rel="bookmark">Is Blazor For Everyone?</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-18" pubdate><a href="/blog/2020/2/18/is-blazor-for-everyone" class="entry-dateline-link">February 18, 2020</a></time> <time class="dt-updated updated" datetime="2020-02-18"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1582037575107" id="item-5e4bf872fd3ee771bded2bfc"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-87b5d5ab155fa296eb8d"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Someone recently asked me why I was so enthusiastic about Blazor. This person didn’t like the programming experience as much as their JavaScript framework. </p><p class="" style="white-space:pre-wrap;">So, here’s the deal. If you are happy with your stack, your tribe, rock on. Blazor obviously isn’t for you. So, who is it for?</p><p class="" style="white-space:pre-wrap;">Blazor is for enterprise developers who are tasked with writing internal applications, B2B, and B2C apps. Yes, many enterprise devs have embraced JavaScript, but many MANY more have not. They don’t like plumbing code. They want to get the job done as quickly and efficiently as possible.</p><p class="" style="white-space:pre-wrap;">Many of these devs thought Silverlight was going to be their path to web-delivered apps. <a href="http://blazorroadshow.com" target="_blank">Everywhere I go</a> I have been pummeled with comments like “Blazor is just Silverlight vNext. Why should we trust Microsoft not to kill it?”</p><p class="" style="white-space:pre-wrap;">This sentiment is understandable, but to conflate Blazor’s future with Silverlight’s past is misinformed.</p><p class="" style="white-space:pre-wrap;"><a href="https://krebsonsecurity.com/tag/steve-jobs/" target="_blank">Silverlight was a casualty of the browser plug-in model</a>. At the time Flash was a very popular plugin for UI, and it was wreaking havoc with iOS devices: iPads and iPhones. Steve Jobs famously announced that Safari would no longer support plugins because of the security concerns over Flash. When developers realized that their Silverlight apps wouldn’t run on the most popular up-and-coming mobile platform, it took the wind out of Silverlight’s sails.</p><p class="" style="white-space:pre-wrap;">By contrast, Blazor runs in the browser on WebAssembly, which is supported by every major browser (Except IE 11), even Safari, even mobile browsers. WebAssembly is here to stay. It’s a standard that has already been adopted. Think of it as an operating system for running compiled code in the browser in the same sandbox that JavaScript runs in. </p><p class="" style="white-space:pre-wrap;">What makes Blazor unique from all of the other languages that run on WebAssembly is the runtime. The mono CLR is compiled to WebAssembly. But it’s a runtime. Any .NET Standard 2.0 code will run there. Other languages are compiled directly to WebAssembly. As far as I know there aren’t any other runtimes for WebAssembly.</p><p class="" style="white-space:pre-wrap;">Silverlight tangent aside, enterprise developers have been waiting for a web programming model that doesn’t drown them in plumbing code. Blazor’s binding model is easy to understand. The component model is elegant. The eventing model is even less ceremonious than standard C#. With tool vendors like <a href="http://devexpress.com/blazor" target="_blank">DevExpress</a> providing controls that do the heavy lifting, these developers have a reason to be delighted.</p><p class="" style="white-space:pre-wrap;">So, rock on my JavaScript friends. Rock on. Maybe Blazor isn’t for you. I happen to know that there’s an army of developers out there who are breathing a sigh of relief.</p><p class="" style="white-space:pre-wrap;">Carl</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/2/18/is-blazor-for-everyone" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e4bf872fd3ee771bded2bfc"></a> <span class="sqs-simple-like" data-item-id="5e4bf872fd3ee771bded2bfc" data-like-count="9"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5e4bf872fd3ee771bded2bfc/1582039156926/" data-record-type="1" data-full-url="/blog/2020/2/18/is-blazor-for-everyone" data-title="Is Blazor For Everyone?"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-15" id="article-5e36cc38a96d977ad2aa0cf0" data-item-id="5e36cc38a96d977ad2aa0cf0"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-02" pubdate><a href="/blog/2020/2/2/reuse-blazor-wasm-ui-in-blazor-server" class="entry-dateline-link">February 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2020/2/2/reuse-blazor-wasm-ui-in-blazor-server" class="u-url" rel="bookmark">Reuse Blazor WASM UI in Blazor Server</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2020-02-02" pubdate><a href="/blog/2020/2/2/reuse-blazor-wasm-ui-in-blazor-server" class="entry-dateline-link">February 2, 2020</a></time> <time class="dt-updated updated" datetime="2020-10-02"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1580649610164" id="item-5e36cc38a96d977ad2aa0cf0"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-f90f0a4526407bad7358"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">So, you’ve decided to use Blazor, but you’re not sure which hosting model to use: Client (WebAssembly) or Server (SignalR-based). Wouldn’t it be great if you could write one version of your app that can run as either a WebAssembly or Server-based Blazor app? With a few tweaks, and by following a few design guidelines, you can.</p><p class="" style="white-space:pre-wrap;">One of the side benefits of this technique is that you can debug your client-side Blazor apps in Visual Studio, something that can’t be done at the time of this writing. That’s because all the code in your Web Assembly project is actually running on the server.</p><p class="" style="white-space:pre-wrap;">I first learned of this pattern at NDC London 2020 in Steve Sanderson’s Blazor talk. The secret sauce is to add a reference from a Server-Side Blazor app to a Client-Side Blazor app, and then register the App component in the Server as a reference to the Client-side App component.</p><p class="" style="white-space:pre-wrap;">Follow these steps and you’ll see for yourself how this could be a game-changer. <a href="http://blazorroadshow.com/BlazorWasm.zip" target="_blank">The complete solution is available for download here</a>.</p><h2 style="white-space:pre-wrap;">Prerequisites</h2><p class="" style="white-space:pre-wrap;">Start by following the instructions in the <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started?tabs=visual-studio&view=aspnetcore-3.1" target="_blank">Microsoft Docs for getting started with Blazor.</a> Install the WebAssembly template as described.</p><h2 style="white-space:pre-wrap;">Create a new Blazor WebAssembly App named BlazorWasm</h2><p class="" style="white-space:pre-wrap;">Make sure to check off “ASP.NET Core hosted”</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_83728"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1024px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:66.40625%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg" data-image-dimensions="1024x680" data-image-focal-point="0.5,0.5" alt="Name your app BlazorWasm" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg" width="1024" height="680" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650280043-ANIW106H2S1PJE72N9A4/image-0.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Name your app BlazorWasm</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_59470"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1024px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:69.3359375%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg" data-image-dimensions="1024x710" data-image-focal-point="0.5,0.5" alt="Select Blazor WebAssembly App and check off “ASP.NET Core hosted”" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg" width="1024" height="710" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650169753-7A3TBRAHFTI8EDDEB3GH/image1.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Select Blazor WebAssembly App and check off “ASP.NET Core hosted”</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_100881"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Add a Blazor Server App to the solution named BlazorServer</h2> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_101455"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1024px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:66.40625%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg" data-image-dimensions="1024x680" data-image-focal-point="0.5,0.5" alt="Name the project BlazorServer" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg" width="1024" height="680" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650472905-UFV8YS66QSBM61MSERNN/image-2.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Name the project BlazorServer</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_105458"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1024px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:69.3359375%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg" data-image-dimensions="1024x710" data-image-focal-point="0.5,0.5" alt="This time, select Blazor Server App" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg" width="1024" height="710" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650542264-MD8W7GJPWFB6NN20DFUH/image-asset.jpeg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">This time, select Blazor Server App</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_132580"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Add a reference from BlazorServer to BlazorWasm.Client and BlazorWasm.Shared</h2><p class="" style="white-space:pre-wrap;">In the BlazorServer project, right click on the project name.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_133528"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:739px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:120.83897399902344%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg" data-image-dimensions="739x893" data-image-focal-point="0.5,0.5" alt="From the BlazorServer project, select Add -&gt; Reference" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg" width="739" height="893" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580650807913-Z6MGMIELH3DZWF43BMMM/image-asset.jpeg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">From the BlazorServer project, select Add -> Reference</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_137384"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Then select the BlazorWasm.Client and BlazorWasm.Shared projects from the Projects menu.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_182137"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:786px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:69.08396911621094%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg" data-image-dimensions="786x543" data-image-focal-point="0.5,0.5" alt="Select both BlazorWasm.Client and BlazorWasm.Shared" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg" width="786" height="543" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651074454-BI4KYPWN120U9QG6KFBP/image-5.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Select both BlazorWasm.Client and BlazorWasm.Shared</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_186049"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Delete Pages in Server app</h2><p class="" style="white-space:pre-wrap;">Now the fun begins. Delete Counter.razor, Error.razor, FetchData.razor, and Index.razor from the BlazorServer project.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_186940"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:437px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:87.18535614013672%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg" data-image-dimensions="437x381" data-image-focal-point="0.5,0.5" alt="Delete these four pages" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg" width="437" height="381" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651226266-WS143HN9UIGGM2F6CG14/image-6.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Delete these four pages</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_190865"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Change the Server App Component to Client App Component</h2><p class="" style="white-space:pre-wrap;">In the BlazorServer project, open _Hosts.cshtml, locate the <app> tag, and change the component definition from this:</p><pre><code> <component type="typeof(App)" render-mode="ServerPrerendered" /></code></pre><p class="" style="white-space:pre-wrap;">to this</p><pre><code> <component type="typeof(BlazorWasm.Client.App)" render-mode="Server" /></code></pre> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_227707"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:984px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:90.54877471923828%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg" data-image-dimensions="984x891" data-image-focal-point="0.5,0.5" alt="Change &lt;app&gt; component to reference the client" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg" width="984" height="891" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651561942-BUOROXQ22GCYC3V0WAPS/image-7.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Change <app> component to reference the client</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_251521"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Set Multiple Start Projects</h2><p class="" style="white-space:pre-wrap;">In order to test this, we have to run the WASM server, which loads the WebAssembly client, and the BlazorServer Server-side project as well. </p><p class="" style="white-space:pre-wrap;">Right-click on the Solution at the top of the Solution Explorer, and Select Properties. Under Common Properties -> Startup Project select “Multiple Startup Projects”, then set the Action for BlazorServer and BlazorWasm.Server to “Start”</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_252800"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:786px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:69.21119689941406%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg" data-image-dimensions="786x544" data-image-focal-point="0.5,0.5" alt="image-8.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg" width="786" height="544" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580651853905-V1Y7BUTJ3ZRDYEX0U0NS/image-8.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_295781"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Rebuild All and Run</h2><p class="" style="white-space:pre-wrap;">You will see two tabs: BlazorServer and BlazorWasm. Both look exactly the same. Try using the Counter page from both. Try going to the Fetch Data page on both. Uh oh. Exception!</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_296857"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1269px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:58.86524963378906%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg" data-image-dimensions="1269x747" data-image-focal-point="0.5,0.5" alt="The Server app doesn’t know about HttpClient." data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg" width="1269" height="747" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652171510-6D73N6MZSKOB1FEYA0VI/image-9.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">The Server app doesn’t know about HttpClient.</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_300784"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">This Exception illustrates how you will need to design your app from now on. The HttpClient class is available in the BlazorWasm.Client project by default, but not in the BlazorServer project. </p><p class="" style="white-space:pre-wrap;">Here’s the rule:</p><blockquote><p class="" style="white-space:pre-wrap;">In order to achieve reusability between these two projects, they must each have references to the same external resources: projects, packages, scripts, etc.</p></blockquote><p class="" style="white-space:pre-wrap;">So, we have to add a scoped HttpClient service to the BlazorServer project. Open the startup.cs file and add the following to the ConfigureServices method:</p><pre><code> services.AddScoped<System.Net.Http.HttpClient>();</code></pre><p class="" style="white-space:pre-wrap;">We’re not done yet. Take a look at the FetchData.razor file in BlazorWasm.Client. It’s trying to access an API controller that’s in the same url scope. In order for this to work in both client and server, we have to be more explicit.</p><p class="" style="white-space:pre-wrap;">In the BlazorWasm.Server project, expand Properties, and then click on launchSettings.json. Copy the SSL port. We’re going to need that. </p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1580649513122_346809"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1167px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:80.20565795898438%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg" data-image-dimensions="1167x936" data-image-focal-point="0.5,0.5" alt="image-10.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg" width="1167" height="936" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580652748348-0YM18EBGY8KKFPIACOZB/image-10.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_374575"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Change the controller URL in FetchData.razor</h2><p class="" style="white-space:pre-wrap;">In the BlazorWasm.Client project, open FetchData.razor under the Pages folder, and change the URL from "WeatherForecast" to “<a href="https://localhost[port]/WeatherForecast”">https://localhost:[port]/WeatherForecast”</a> replacing “[port]” with the SSL port from launchSerttings.json:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1580649513122_400019"><div class="sqs-block-content"><pre class="source-code">@<span class="cm-variable">code</span>{ <span class="cm-variable">private</span> <span class="cm-variable">WeatherForecast</span>[] <span class="cm-variable">forecasts</span>; <span class="cm-variable">protected</span> <span class="cm-variable">override</span> <span class="cm-keyword">async</span> <span class="cm-variable">Task</span> <span class="cm-variable">OnInitializedAsync</span>() { <span class="cm-variable">string</span> <span class="cm-variable">url</span> <span class="cm-operator">=</span> <span class="cm-string">"https://localhost:44306/WeatherForecast"</span>; <span class="cm-variable">forecasts</span> <span class="cm-operator">=</span> <span class="cm-keyword">await</span> <span class="cm-variable">Http</span>.<span class="cm-property">GetJsonAsync</span><span class="cm-operator"><</span><span class="cm-variable">WeatherForecast</span>[]<span class="cm-operator">></span>(<span class="cm-variable">url</span>); } } </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1580649513122_426984"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Now when you run the app, you can access the Weather Data from both the Server-side and Client-side blazor apps. </p><p class="" style="white-space:pre-wrap;">If you use this pattern, your server will use the exact same patterns and code that the client uses. That may not always be appropriate. So, make sure you can commit to an architecture that puts the client design first.</p><h2 style="white-space:pre-wrap;">Other Resources</h2><p class="" style="white-space:pre-wrap;">Come see me in a city near you on my <a href="http://blazorroadshow.com" target="_blank">Blazor Roadshow</a> world tour</p><p class="" style="white-space:pre-wrap;">Attend my <a href="http://blazor.appvnext.com" target="_blank">one-day online Blazor workshop</a> to dig into Blazor big time.</p><p class="" style="white-space:pre-wrap;">Check out <a href="https://www.devexpress.com/blazor/" target="_blank">DevExpress Blazor Components</a>, which are currently free.</p><p class="" style="white-space:pre-wrap;">Happy coding!</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2020/2/2/reuse-blazor-wasm-ui-in-blazor-server" class="sqs-comment-link sqs-disqus-comment-link" data-id="5e36cc38a96d977ad2aa0cf0"></a> <span class="sqs-simple-like" data-item-id="5e36cc38a96d977ad2aa0cf0" data-like-count="5"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="1580658235630-HU7VLNY51EAS4TW9V5CL" data-asset-url="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1580658235630-HU7VLNY51EAS4TW9V5CL/CarlToon.png" data-record-type="1" data-full-url="/blog/2020/2/2/reuse-blazor-wasm-ui-in-blazor-server" data-title="Reuse Blazor WASM UI in Blazor Server"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-16" id="article-5dfe00693292313144b0a1a0" data-item-id="5dfe00693292313144b0a1a0"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-12-21" pubdate><a href="/blog/2019/12/21/logging-signalr-traffic-in-server-side-blazor" class="entry-dateline-link">December 21, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-21"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2019/12/21/logging-signalr-traffic-in-server-side-blazor" class="u-url" rel="bookmark">Logging SignalR Traffic in Server-Side Blazor</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-12-21" pubdate><a href="/blog/2019/12/21/logging-signalr-traffic-in-server-side-blazor" class="entry-dateline-link">December 21, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-21"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1576927627724" id="item-5dfe00693292313144b0a1a0"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-f9059ff128f1b083ee17"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Server-Side Blazor is indeed magical due to a hidden communications system keeping a persistent connection between the browser and the server: SignalR. SignalR establishes a circuit for every client and keeps a server-side model of the DOM in memory. The app sends messages when interactions happen in the browser, such as a button click. The server receives those messages and executes whatever code it needs to, such as a button click handler, applies it to the graph, calculates the difference, and sends messages back to the app which updates the DOM. It’s brilliant, really. However, being hidden, it piques our curiosity as to what’s being sent and received.</p><p class="" style="white-space:pre-wrap;">As I mentioned, this SignalR system is hidden from you as a developer. You can’t just open up the browser tools and watch the traffic. However, with a little tweaking, you can listen in on the conversation and you might be surprised at how <em>little </em>data is being transmitted.</p><p class="" style="white-space:pre-wrap;">The secret sauce is in how you configure the logging provider and filters. Let me show you how. </p><p class="" style="white-space:pre-wrap;">Create a new Server-Side Blazor app in Visual Studio 2019 and in the <em>program.cs</em> file change the CreateHostBuilder call to this:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_5002"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">public</span> <span class="cm-variable">static</span> <span class="cm-variable">IHostBuilder</span> <span class="cm-variable">CreateHostBuilder</span>(<span class="cm-variable">string</span>[] <span class="cm-variable">args</span>) <span class="cm-operator">=></span> <span class="cm-variable">Host</span>.<span class="cm-property">CreateDefaultBuilder</span>(<span class="cm-variable">args</span>) .<span class="cm-property">ConfigureLogging</span>(<span class="cm-def">logging</span> <span class="cm-operator">=></span> { <span class="cm-variable-2">logging</span>.<span class="cm-property">ClearProviders</span>(); <span class="cm-variable-2">logging</span>.<span class="cm-property">AddConsole</span>(); <span class="cm-variable-2">logging</span>.<span class="cm-property">AddFilter</span>( <span class="cm-string">"Microsoft.AspNetCore.SignalR"</span>, <span class="cm-variable">LogLevel</span>.<span class="cm-property">Trace</span>); <span class="cm-variable-2">logging</span>.<span class="cm-property">AddFilter</span>( <span class="cm-string">"Microsoft.AspNetCore.Http.Connections"</span>, <span class="cm-variable">LogLevel</span>.<span class="cm-property">Trace</span>); }) .<span class="cm-property">ConfigureWebHostDefaults</span>(<span class="cm-def">webBuilder</span> <span class="cm-operator">=></span> { <span class="cm-variable-2">webBuilder</span>.<span class="cm-property">UseStartup</span><span class="cm-operator"><</span><span class="cm-variable">Startup</span><span class="cm-operator">></span>(); });</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_9763"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Run the app and open the <em>Output</em> debug window.</p><p class="" style="white-space:pre-wrap;">Select the application from the <em>Output </em>window’s “Show Output From” dropdown.</p><p class="" style="white-space:pre-wrap;">As you use the app, you can watch the traffic in the <em>Output </em>window.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576927628504_15963"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1920px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:56.25%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png" data-image-dimensions="1920x1080" data-image-focal-point="0.5,0.5" alt="Select your application in the Output window’s “Show Output From” dropdown list." data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png" width="1920" height="1080" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576928223896-9QU74J5Q14XL2KRZ49WM/screen.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Select your application in the <em>Output </em>window’s “Show Output From” dropdown list.</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_17328"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Let’s take a look at the output. The first thing you see is IIS Express registering the app. No big whoop.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_17814"><div class="sqs-block-content"><pre class="source-code">Starting IIS Express ... Successfully registered URL "http://localhost:50115/" for site "BlazorApp1" application "/" Successfully registered URL "https://localhost:44389/" for site "BlazorApp1" application "/" Registration completed for site "BlazorApp1" IIS Express is running.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_18380"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next you see services for json and blazorpack being registered.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_18829"><div class="sqs-block-content"><pre class="source-code">dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubProtocolResolver[1] Registered SignalR Protocol: json, implemented by Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubProtocolResolver[1] Registered SignalR Protocol: blazorpack, implemented by Microsoft.AspNetCore.Components.Server.BlazorPack.BlazorPackHubProtocol.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_27649"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next you see the hub methods Blazor uses bing bound:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_28238"><div class="sqs-block-content"><pre class="source-code">trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'StartCircuit' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'ConnectCircuit' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'BeginInvokeDotNetFromJS' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'EndInvokeJSFromDotNet' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'DispatchBrowserEvent' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'OnRenderCompleted' is bound. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[9] 'ComponentHub' hub method 'OnLocationChanged' is bound.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_37453"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, we see the “heartbeat” being established. If you sit and do nothing, you’ll see the browser “ping” the server to keep the connection alive.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_38062"><div class="sqs-block-content"><pre class="source-code">trce: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager[9] Starting connection heartbeat.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_48660"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">After a few lines saying that the app has started you see the actual WebSocket being opened with the browser.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_49459"><div class="sqs-block-content"><pre class="source-code">dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager[1] New connection SYQgFSqfV_gwlp9aOtZ-FQ created. dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[10] Sending negotiation response. dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[4] Establishing new connection. dbug: Microsoft.AspNetCore.SignalR.HubConnectionHandler[5] OnConnectedAsync started. dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[1] Socket opened using Sub-Protocol: '(null)'.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_50034"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, a little handshake. I don’t claim to know exactly what’s being said here, but 38 bytes are received using a “hub protocol” defined in blazorpack. <a href="https://github.com/aspnet/AspNetCore/tree/master/src/Components/Server/src/BlazorPack" target="_blank">Blazorpack</a> is an internal layer that provides access to the hub methods and provides serialization/deserialization services, etc.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_50513"><div class="sqs-block-content"><pre class="source-code">trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Text, size: 38, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubProtocolResolver[2] Found protocol implementation for requested protocol: blazorpack. dbug: Microsoft.AspNetCore.SignalR.HubConnectionContext[1] Completed connection handshake. Using HubProtocol 'blazorpack'. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 3 bytes. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 491, EndOfMessage: True.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_63162"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Next, the “StartCircuit” method is invoked. It’s a bit of gobbledygook, but this is where things actually get interesting.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_63988"><div class="sqs-block-content"><pre class="source-code">dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "0", Target: "StartCircuit", Arguments: [ https://localhost:44389/, https://localhost:44389/, [] ], StreamIds: [ ] }.</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_64561"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Here I’m showing the log entries that were generated when I clicked on the “Fetch Data” page:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_65165"><div class="sqs-block-content"><pre class="source-code">trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 353, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "DispatchBrowserEvent", Arguments: [ , ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 169 bytes. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnLocationChanged", Arguments: [ https://localhost:44389/fetchdata, True ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 27, EndOfMessage: True. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 3528 bytes. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 133 bytes. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 140 bytes. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnRenderCompleted", Arguments: [ 7, ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 27, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnRenderCompleted", Arguments: [ 8, ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 27, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnRenderCompleted", Arguments: [ 9, ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 27, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnRenderCompleted", Arguments: [ 10, ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_65740"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">It looks like the browser is telling Blazor that the mouse was clicked at an X of 106 and a Y of 202. It wraps this in a “DispatchBrowserEvent” message.</p><p class="" style="white-space:pre-wrap;">Next an “OnLocationChanged” message is sent passing the url https://localhost:44389/fetchdata</p><p class="" style="white-space:pre-wrap;">That’s a bit different than standard REST traffic, isn’t it? That’s the point of server-side Blazor. It’s not a REST app. All the communication happens via SignalR, even simple navigation. Wow.</p><p class="" style="white-space:pre-wrap;">Four more messages are sent, which result in “OnRenderCompleted” actions. All in all, about 4K of data is sent, including the weather data itself.</p><p class="" style="white-space:pre-wrap;">Let’s watch what happens when we click the counter button on the Counter page. To measure this, go to the Counter page, clear the output window, and click the button. Here’s what you see.</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1576927628504_67889"><div class="sqs-block-content"><pre class="source-code">trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 292, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "DispatchBrowserEvent", Arguments: [ , ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[11] Sending payload: 148 bytes. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. trce: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[9] Message received. Type: Binary, size: 27, EndOfMessage: True. dbug: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[1] Received hub invocation: InvocationMessage { InvocationId: "", Target: "OnRenderCompleted", Arguments: [ 7, ], StreamIds: [ ] }. trce: Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher[7] InvocationId (null): Sending result of type 'System.Void'. </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576927628504_68506"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Not much at all being sent here. I counted about 500 bytes from what I can see in the log.</p><p class="" style="white-space:pre-wrap;">This stuff is fascinating to me. I don’t pretend to know what exactly is being said, but the conversation is definitely terse.</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2019/12/21/logging-signalr-traffic-in-server-side-blazor" class="sqs-comment-link sqs-disqus-comment-link" data-id="5dfe00693292313144b0a1a0"></a> <span class="sqs-simple-like" data-item-id="5dfe00693292313144b0a1a0" data-like-count="3"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5dfe00693292313144b0a1a0/1576933520739/" data-record-type="1" data-full-url="/blog/2019/12/21/logging-signalr-traffic-in-server-side-blazor" data-title="Logging SignalR Traffic in Server-Side Blazor"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-17" id="article-5df146b0c83d0e1e4d72e17d" data-item-id="5df146b0c83d0e1e4d72e17d"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-12-11" pubdate><a href="/blog/2019/12/11/get-a-windows-10-pro-pc-for-160" class="entry-dateline-link">December 11, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-11"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2019/12/11/get-a-windows-10-pro-pc-for-160" class="u-url" rel="bookmark">Get a Windows 10 Pro PC for $160!</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-12-11" pubdate><a href="/blog/2019/12/11/get-a-windows-10-pro-pc-for-160" class="entry-dateline-link">December 11, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-11"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1576093389180" id="item-5df146b0c83d0e1e4d72e17d"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-dcbe8b1d594425451f43"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">I recently put together a small network that required a couple very lightweight PCs running Windows 10. That’s when I stumbled across this little gem, the <a href="https://www.amazon.com/gp/product/B07MB9TC33" target="_blank">W5 Pro Mini PC</a> which I picked up on Amazon for only $160!</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576089305219_118170"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1500px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:100%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg" data-image-dimensions="1500x1500" data-image-focal-point="0.5,0.5" alt="The W5 Pro Mini PC is suprisingly poweful for less than $200!" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg" width="1500" height="1500" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576090054718-ZBXFL9H8NGKH9JFN40L7/w5.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">The W5 Pro Mini PC is suprisingly poweful for less than $200!</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576089305219_122140"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">What you get</h2><ul data-rte-list="default"><li><p class="" style="white-space:pre-wrap;">Intel Atom Z8350 Processor (1.44 GHz)</p></li><li><p class="" style="white-space:pre-wrap;">Powered by Micro USB</p></li><li><p class="" style="white-space:pre-wrap;">Windows 10 Pro x64 Preinstalled</p></li><li><p class="" style="white-space:pre-wrap;">4GB DDR RAM</p></li><li><p class="" style="white-space:pre-wrap;">64GB eMMC Storage</p></li><li><p class="" style="white-space:pre-wrap;">Supports a Micro SD card up to 128GB</p></li><li><p class="" style="white-space:pre-wrap;">Built in 2.4G/5G AC WiFi and Bluetooth 4.2</p></li><li><p class="" style="white-space:pre-wrap;">Intel HD graphics</p></li><li><p class="" style="white-space:pre-wrap;">Video supports 4K HD</p></li><li><p class="" style="white-space:pre-wrap;">Two USB ports (one 2.0 and one 3.0)</p></li></ul><h2 style="white-space:pre-wrap;">Streaming Video</h2><p class="" style="white-space:pre-wrap;">As a smart TV (access to all the services) it does a good job of keeping up with streaming video. It’s not as smooth as my MacBook but you can’t beat it for the money!</p><h2 style="white-space:pre-wrap;">Travel PC</h2><p class="" style="white-space:pre-wrap;">This is a great PC to take with you when you travel if using your hotel TV is acceptable. Just throw a keyboard and mouse in your suitcase.</p><h1 style="white-space:pre-wrap;">Installation</h1><p class="" style="white-space:pre-wrap;">Follow these simple steps and you’ll be up and running in no time!</p><h2 style="white-space:pre-wrap;">Initial Setup</h2><p class="" style="white-space:pre-wrap;">It’s very easy. Connect a keyboard and mouse via USB, plug it into a TV or monitor, power it up, and follow the prompts to set up Windows 10 Professional x64.</p><h2 style="white-space:pre-wrap;">Check for Updates</h2><p class="" style="white-space:pre-wrap;">Whenever I install Windows I immediately go get all the updates and make sure that the update schedule works and that I don’t have to babysit it.</p><p class="" style="white-space:pre-wrap;">Type <em>Check for Updates </em>in the Windows Search Bar found in the lower-left of the screen. Follow all of the instructions and don’t do anything else until the updates are all installed.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_6697"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:810px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:96.04937744140625%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png" data-image-dimensions="810x778" data-image-focal-point="0.5,0.5" alt="updates.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png" width="810" height="778" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091174947-GIJCJTGDUE5JAB6P3KAG/updates.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_7961"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Turn on Auto Login Feature</h2><p class="" style="white-space:pre-wrap;">This is another step I take for every PC in my house. Since I’m not worried about someone else logging into it, I turn on the auto-login feature, so when the machine boots up it automatically logs me into Windows.</p><p class="" style="white-space:pre-wrap;">To enable this, type <em>netplwiz </em>in the Windows Search bar.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_16040"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:463px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:110.15118408203125%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png" data-image-dimensions="463x510" data-image-focal-point="0.5,0.5" alt="auto-login.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png" width="463" height="510" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091424342-AWDEFBTXAAAMXA61LL5P/auto-login.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_17098"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Deselect the checkbox next to “Users must enter a user name and password to use this computer.” Enter your username and password. Hit <em>Apply </em>and then <em>OK. </em>That’s it.</p><h2 style="white-space:pre-wrap;">Allow Remote Access</h2><p class="" style="white-space:pre-wrap;">This is a nice option if you don’t want to dedicate a monitor, mouse, and keyboard to the PC. In my case, I have two of these running in a box, so I can’t really plug them into a monitor. Remote Access is a nice feature that allows me to connect remotely.</p><h3 style="white-space:pre-wrap;">First step: Name your PC</h3><p class="" style="white-space:pre-wrap;">Type <em>This PC</em> in the Windows Search bar, then right-click on the icon that says “This PC” in the File Explorer, and then select “Properties”. You’ll see a window like this:</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_33715"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:681px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:113.06902313232422%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png" data-image-dimensions="681x770" data-image-focal-point="0.5,0.5" alt="This PC Properties" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png" width="681" height="770" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576095822900-F18SH5RTCWFT9Q8TL6P9/mantle.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">This PC Properties</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_34949"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">I have named my PC <em>MANTLE. </em>To change your PC name, click the “Change Settings” link next to the Computer name. Pick a name you’ll remember.</p><h3 style="white-space:pre-wrap;">Next Step: Allow Remote Access</h3><p class="" style="white-space:pre-wrap;">To allow remote access, type "<em>Remote Desktop Settings” </em>into the Windows Search Bar and turn on Remote Acccess:</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_36062"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:502px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:96.21514129638672%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png" data-image-dimensions="502x483" data-image-focal-point="0.5,0.5" alt="Remote Access allows you to connect to this PC without a dedicated monitor, mouse, and keyboard." data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png" width="502" height="483" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576096133577-TRKUVP7ELTHUBMFUVUKQ/remote.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">Remote Access allows you to connect to this PC without a dedicated monitor, mouse, and keyboard.</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_37275"><div class="sqs-block-content"> <div class="sqs-html-content"> <h2 style="white-space:pre-wrap;">Extras!</h2><p class="" style="white-space:pre-wrap;">These are some items that I added to my PC. I chose them because of the positive reviews on Amazon. They work well, and they are affordable.</p><h3 style="white-space:pre-wrap;"><a href="https://www.amazon.com/gp/product/B07FH7XJCD" target="_blank">BYEASY 4 Port USB 3.0 Hub</a> ($12.99)</h3><p class="" style="white-space:pre-wrap;">This was a no-brainer. <a href="https://www.amazon.com/gp/product/B07FH7XJCD" target="_blank">The ByEasy Hub</a> allows me to connect a total of four USB 3 devices and is completely USB-powered. Done.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_59782"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1500px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:100%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg" data-image-dimensions="1500x1500" data-image-focal-point="0.5,0.5" alt="$13 Hub. Big Bang for the Buck" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg" width="1500" height="1500" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091683075-GA0FHPF4X2SL4Y3KVED4/hub.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> <figcaption class="image-caption-wrapper"> <div class="image-caption"><p class="">$13 Hub. Big Bang for the Buck</p></div> </figcaption> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_74081"><div class="sqs-block-content"> <div class="sqs-html-content"> <h3 style="white-space:pre-wrap;"><a href="https://www.amazon.com/gp/product/B00Q4WQ7XW" target="_blank">Optimal Shop USB 2.0 External Sound Card</a> ($14.59)</h3><p class="" style="white-space:pre-wrap;">If you need 3.5mm audio inputs and outputs, I recommend this little gem. It’s totally USB powered, it has a line input, a very important feature for me, and it’s lightweight. It also has surround sound outputs, but I don’t use them in this project.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1576094520418_75143"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1500px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:100%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg" data-image-dimensions="1500x1500" data-image-focal-point="0.5,0.5" alt="usbaudio.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg" width="1500" height="1500" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1576091867413-MBCFY0O4KJ3393P4BALJ/usbaudio.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1576094520418_76297"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">The best part? for a whopping total of <strong>$187.57 </strong>I have a decent PC with 4 USB 3 jacks, a line input, and surround sound support. You just can’t beat that… for now!</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2019/12/11/get-a-windows-10-pro-pc-for-160" class="sqs-comment-link sqs-disqus-comment-link" data-id="5df146b0c83d0e1e4d72e17d"></a> <span class="sqs-simple-like" data-item-id="5df146b0c83d0e1e4d72e17d" data-like-count="1"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5df146b0c83d0e1e4d72e17d/1576097105837/" data-record-type="1" data-full-url="/blog/2019/12/11/get-a-windows-10-pro-pc-for-160" data-title="Get a Windows 10 Pro PC for $160!"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-18" id="article-5de01d74d36d6241f148a320" data-item-id="5de01d74d36d6241f148a320"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-11-28" pubdate><a href="/blog/2019/11/28/inotifypropertychanged-behavior-in-blazor-components" class="entry-dateline-link">November 28, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-04"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2019/11/28/inotifypropertychanged-behavior-in-blazor-components" class="u-url" rel="bookmark">INotifyPropertyChanged behavior in Blazor Components</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-11-28" pubdate><a href="/blog/2019/11/28/inotifypropertychanged-behavior-in-blazor-components" class="entry-dateline-link">November 28, 2019</a></time> <time class="dt-updated updated" datetime="2019-12-04"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1575124669817" id="item-5de01d74d36d6241f148a320"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-d6fa7fac205245894513"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">By design, when you build a server-side Blazor app, and you pass parameters to a component when those parameters change inside the component the changes are not reflected in the host. With a little hack, we can make that happen. It’s INotifyPropertyChanged all over again!</p><p class="" style="white-space:pre-wrap;">This gem came from my <a href="http://blazor.appvnext.com">online 8-hour Blazor Workshop</a>. You can purchase the video and materials or sign up for the next class.</p><p class="" style="white-space:pre-wrap;">In Visual Studio 2019 (or higher) create a new Blazor app called <strong>NotifyComponentDemo</strong>.,</p><p class="" style="white-space:pre-wrap;">Add a new class called Customer with two string properties: <strong>Name </strong>and <strong>Email</strong>.</p><p class="" style="white-space:pre-wrap;">Right-click on the Pages folder and select <strong>Add / New Item</strong>.</p><p class="" style="white-space:pre-wrap;">Select <strong>Web </strong>from the list on the left and then <strong>Razor Comopnent</strong> from the list.on the right.</p><p class="" style="white-space:pre-wrap;">Name the component <strong>Editor</strong> and replace with the following:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1574968680166_16444"><div class="sqs-block-content"><pre class="source-code"><span class="cm-tag cm-bracket"><</span><span class="cm-tag">div</span> <span class="cm-attribute">style</span>=<span class="cm-string">"background-color:lightgray;"</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">div</span> <span class="cm-attribute">style</span>=<span class="cm-string">"padding:1vw;"</span><span class="cm-tag cm-bracket">></span> Name: <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">input</span> <span class="cm-attribute">@bind</span>=<span class="cm-string">"@Customer.Name"</span> <span class="cm-attribute">@bind:event</span>=<span class="cm-string">"oninput"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span><span class="cm-tag cm-bracket">/></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span><span class="cm-tag cm-bracket">/></span> Email: <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">input</span> <span class="cm-attribute">@bind</span>=<span class="cm-string">"@Customer.Email"</span> <span class="cm-attribute">@bind:event</span>=<span class="cm-string">"oninput"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">div</span><span class="cm-tag cm-bracket">></span> <span class="cm-tag cm-bracket"></</span><span class="cm-tag">div</span><span class="cm-tag cm-bracket">></span> @code { [Parameter] public Customer Customer { get; set; } } </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1574968680166_17696"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Now open <strong>Index.razor</strong> from the Pages folder and replace wiht the following:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1574968680166_18290"><div class="sqs-block-content"><pre class="source-code">@page "/" <span class="cm-tag cm-bracket"><</span><span class="cm-tag">Editor</span> <span class="cm-attribute">Customer</span>=<span class="cm-string">"Customer"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> Name: @Customer.Name <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> Email: @Customer.Email <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> @code { Customer Customer = new Customer(); protected override void OnInitialized() { Customer = new Customer { Name = "Ben Goofin", Email = "ben@bengoofin.com" }; } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1574968680166_19425"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Our component accepts a customer object as a parameter. It allow you to edit the name and email properties. </p><p class="" style="white-space:pre-wrap;">Press F5 and run. One might expect that as you edit the values, your Index page would show the changes as you type, especially since we are using the @bind:event=oninput syntax. </p><p class="" style="white-space:pre-wrap;">Not so, and this is by design.</p><p class="" style="white-space:pre-wrap;">In order to make this happen we have to first capture the keystrokes in the input boxes. Change the input tags to the following:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1575124670644_10873"><div class="sqs-block-content"><pre class="source-code"> Name: <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">input</span> <span class="cm-attribute">@bind</span>=<span class="cm-string">"@Customer.Name"</span> <span class="cm-attribute">@bind:event</span>=<span class="cm-string">"oninput"</span> <span class="cm-attribute">@onkeyup</span>=<span class="cm-string">"KeyPressed"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span><span class="cm-tag cm-bracket">/></span><span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span><span class="cm-tag cm-bracket">/></span> Email: <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">input</span> <span class="cm-attribute">@bind</span>=<span class="cm-string">"@Customer.Email"</span> <span class="cm-attribute">@bind:event</span>=<span class="cm-string">"oninput"</span> <span class="cm-attribute">@onkeyup</span>=<span class="cm-string">"KeyPressed"</span> <span class="cm-tag cm-bracket">/></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1575124670644_11573"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Now we need to handle that keypress event, but what should we do when a key is pressed?</p><p class="" style="white-space:pre-wrap;">We need to add an event handler to notify the host (Index.razor) that the state of the component (namely the Customer object) has changed. </p><p class="" style="white-space:pre-wrap;">Add the following code to the code block in the component:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1575124670644_12603"><div class="sqs-block-content"><pre class="source-code"> [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">EventCallback</span><span class="cm-operator"><</span><span class="cm-variable">string</span><span class="cm-operator">></span> <span class="cm-variable">ComponentDataUpdated</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-keyword">async</span> <span class="cm-variable">Task</span> <span class="cm-variable">KeyPressed</span>(<span class="cm-variable">KeyboardEventArgs</span> <span class="cm-variable">args</span>) { <span class="cm-keyword">await</span> <span class="cm-variable">ComponentDataUpdated</span>.<span class="cm-property">InvokeAsync</span>(<span class="cm-string">""</span>); }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1575124670644_13255"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Go back to the host (Index.razor) and modify as follows:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1575124670644_13775"><div class="sqs-block-content"><pre class="source-code">@page "/" <span class="cm-tag cm-bracket"><</span><span class="cm-tag">Editor</span> <span class="cm-attribute">Customer</span>=<span class="cm-string">"Customer"</span> <span class="cm-attribute">ComponentDataUpdated</span>=<span class="cm-string">"ComponentUpdated"</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> Name: @Customer.Name <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> Email: @Customer.Email <span class="cm-tag cm-bracket"><</span><span class="cm-tag">br</span> <span class="cm-tag cm-bracket">/></span> @code { Customer Customer = new Customer(); protected override void OnInitialized() { Customer = new Customer { Name = "Ben Goofin", Email = "ben@bengoofin.com" }; } void ComponentUpdated(string args) { StateHasChanged(); } } </pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1575124670644_14399"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">So, on every key press our component raises the ComponentUpdated event, which we handle in the host page in a like-named method, ComponentUpdated. The string argument is ignored but necessary.</p><p class="" style="white-space:pre-wrap;">All our code does is call StateHasChanged() and magically, Blazor updates the UI in the host that displays properties of the Customer object.</p><p class="" style="white-space:pre-wrap;">Voila'</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2019/11/28/inotifypropertychanged-behavior-in-blazor-components" class="sqs-comment-link sqs-disqus-comment-link" data-id="5de01d74d36d6241f148a320"></a> <span class="sqs-simple-like" data-item-id="5de01d74d36d6241f148a320" data-like-count="1"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5de01d74d36d6241f148a320/1575439988263/" data-record-type="1" data-full-url="/blog/2019/11/28/inotifypropertychanged-behavior-in-blazor-components" data-title="INotifyPropertyChanged behavior in Blazor Components"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-19" id="article-5da74dca30ee601884745644" data-item-id="5da74dca30ee601884745644"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-10-16" pubdate><a href="/blog/2019/10/16/building-a-reusable-generic-template-component-for-server-side-blazor" class="entry-dateline-link">October 16, 2019</a></time> <time class="dt-updated updated" datetime="2019-11-26"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2019/10/16/building-a-reusable-generic-template-component-for-server-side-blazor" class="u-url" rel="bookmark">Building a reusable generic template component for server-side Blazor</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2019-10-16" pubdate><a href="/blog/2019/10/16/building-a-reusable-generic-template-component-for-server-side-blazor" class="entry-dateline-link">October 16, 2019</a></time> <time class="dt-updated updated" datetime="2019-11-26"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1571246240149" id="item-5da74dca30ee601884745644"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-387039f993b7ed67ff9e"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">The goal is to develop a little bit of UI that can be reused in different Blazor projects or with different data. The component knows nothing about the class it’s working with. The Type is supplied by a generic parameter.</p><p class="" style="white-space:pre-wrap;">This component was developed for my <a href="http://blazor.appvnext.com">Server-Side Blazor Workshop</a> but I’m making it freely available because it answers so many fundamental questions about Blazor.</p><p class="" style="white-space:pre-wrap;"><a href="https://drive.google.com/file/d/1WAMBt0zn1vecF57b_IAgSr9nz9O3oiCC/view?usp=sharing">Download the code with sample project here.</a></p><p class="" style="white-space:pre-wrap;">Imagine you have a master list of some type of item, and you want the user to be able to select one or more of them for whatever reason. Here is what the UI looks like in our demo:</p> </div> </div></div><div class="sqs-block image-block sqs-block-image" data-block-type="5" id="block-yui_3_17_2_1_1571245501717_16161"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline combination-animation-none individual-animation-none individual-text-animation-none " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:790px;" > <div class="image-block-wrapper" data-animation-role="image" > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:29.8734188079834%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg" data-image-dimensions="790x236" data-image-focal-point="0.5,0.5" alt="objectpicker.jpg" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg" width="790" height="236" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1571245884752-EXAL0RKGE06LGJLQ7K5I/objectpicker.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571257280853_42660"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">The component name is ObjectPicker. Here is the complete source:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_65132"><div class="sqs-block-content"><pre class="source-code">@<span class="cm-variable">typeparam</span> <span class="cm-variable">TItem</span> <span class="cm-operator"><</span><span class="cm-variable">table</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%"</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">tr</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:45%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-variable">All</span> @<span class="cm-variable">ItemTypePlural</span><span class="cm-operator"><</span><span class="cm-variable">br</span> <span class="cm-operator">/></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:10%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">span</span><span class="cm-operator">>&</span><span class="cm-variable">nbsp</span>;<span class="cm-operator"><</span><span class="cm-string-2">/span></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:45%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-variable">Selected</span> @<span class="cm-variable">ItemTypePlural</span><span class="cm-operator"><</span><span class="cm-variable">br</span> <span class="cm-operator">/></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-string-2">/tr></span> <span class="cm-operator"><</span><span class="cm-variable">tr</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:45%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">select</span> @<span class="cm-variable">ondblclick</span><span class="cm-operator">=</span><span class="cm-string">"ItemDblClickedFromAllItems"</span> @<span class="cm-variable">onchange</span><span class="cm-operator">=</span><span class="cm-string">"ItemSelectedFromAllItems"</span> <span class="cm-variable">size</span><span class="cm-operator">=</span><span class="cm-string">"10"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> @<span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">Item</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span>) { <span class="cm-keyword">if</span> (@<span class="cm-variable">ItemValue</span>(<span class="cm-variable">Item</span>) <span class="cm-operator">==</span> @<span class="cm-variable">ItemValue</span>(<span class="cm-variable">SelectedItem</span>)) { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">selected</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } <span class="cm-keyword">else</span> { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } } <span class="cm-operator"><</span><span class="cm-string-2">/select></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:10%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">button</span> @<span class="cm-variable">onclick</span><span class="cm-operator">=</span><span class="cm-string">"AddSelectedItem"</span> <span class="cm-variable">type</span><span class="cm-operator">=</span><span class="cm-string">"button"</span> <span class="cm-variable">disabled</span><span class="cm-operator">=</span><span class="cm-string">"@AddSelectedItemButtonDisabled"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> <span class="cm-operator">&</span><span class="cm-variable">gt</span>; <span class="cm-operator"><</span><span class="cm-string-2">/button><br /</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">button</span> @<span class="cm-variable">onclick</span><span class="cm-operator">=</span><span class="cm-string">"@AddAllItems"</span> <span class="cm-variable">type</span><span class="cm-operator">=</span><span class="cm-string">"button"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> <span class="cm-operator">&</span><span class="cm-variable">gt</span>;<span class="cm-operator">&</span><span class="cm-variable">gt</span>; <span class="cm-operator"><</span><span class="cm-string-2">/button><br /</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">button</span> @<span class="cm-variable">onclick</span><span class="cm-operator">=</span><span class="cm-string">"@RemoveSelectedItem"</span> <span class="cm-variable">type</span><span class="cm-operator">=</span><span class="cm-string">"button"</span> <span class="cm-variable">disabled</span><span class="cm-operator">=</span><span class="cm-string">"@RemoveSelectedItemButtonDisabled"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> <span class="cm-operator">&</span><span class="cm-variable">lt</span>; <span class="cm-operator"><</span><span class="cm-string-2">/button><br /</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">button</span> @<span class="cm-variable">onclick</span><span class="cm-operator">=</span><span class="cm-string">"@RemoveAllItems"</span> <span class="cm-variable">type</span><span class="cm-operator">=</span><span class="cm-string">"button"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> <span class="cm-operator">&</span><span class="cm-variable">lt</span>;<span class="cm-operator">&</span><span class="cm-variable">lt</span>; <span class="cm-operator"><</span><span class="cm-string-2">/button><br /</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-variable">td</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:45%;"</span> <span class="cm-variable">valign</span><span class="cm-operator">=</span><span class="cm-string">"top"</span><span class="cm-operator">></span> <span class="cm-operator"><</span><span class="cm-variable">select</span> @<span class="cm-variable">ondblclick</span><span class="cm-operator">=</span><span class="cm-string">"ItemDblClickedFromSelectedItems"</span> @<span class="cm-variable">onchange</span><span class="cm-operator">=</span><span class="cm-string">"ItemSelectedFromSelectedItems"</span> <span class="cm-variable">size</span><span class="cm-operator">=</span><span class="cm-string">"10"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> @<span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">Item</span> <span class="cm-keyword">in</span> <span class="cm-variable">SelectedItems</span>) { <span class="cm-keyword">if</span> (@<span class="cm-variable">ItemValue</span>(<span class="cm-variable">Item</span>) <span class="cm-operator">==</span> @<span class="cm-variable">ItemValue</span>(<span class="cm-variable">SelectedItem</span>)) { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">selected</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } <span class="cm-keyword">else</span> { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } } <span class="cm-operator"><</span><span class="cm-string-2">/select></span> <span class="cm-operator"><</span><span class="cm-string-2">/td></span> <span class="cm-operator"><</span><span class="cm-string-2">/tr></span> <span class="cm-operator"><</span><span class="cm-string-2">/table></span> @<span class="cm-variable">code</span> { [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemType</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemTypePlural</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">TextPropertyName</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">ValuePropertyName</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">List</span><span class="cm-operator"><</span><span class="cm-variable">TItem</span><span class="cm-operator">></span> <span class="cm-variable">AllItems</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">List</span><span class="cm-operator"><</span><span class="cm-variable">TItem</span><span class="cm-operator">></span> <span class="cm-variable">SelectedItems</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">EventCallback</span><span class="cm-operator"><</span><span class="cm-variable">string</span><span class="cm-operator">></span> <span class="cm-variable">ComponentUpdated</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">TItem</span> <span class="cm-variable">SelectedItem</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } <span class="cm-variable">bool</span> <span class="cm-variable">AddSelectedItemButtonDisabled</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>; <span class="cm-variable">bool</span> <span class="cm-variable">RemoveSelectedItemButtonDisabled</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>; <span class="cm-variable">private</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">TItem</span> <span class="cm-variable">Item</span>) { <span class="cm-keyword">return</span> <span class="cm-variable">Item</span>.<span class="cm-property">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">Item</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>(); } <span class="cm-variable">private</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemText</span>(<span class="cm-variable">TItem</span> <span class="cm-variable">Item</span>) { <span class="cm-keyword">return</span> <span class="cm-variable">Item</span>.<span class="cm-property">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">TextPropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">Item</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>(); } <span class="cm-variable">protected</span> <span class="cm-variable">override</span> <span class="cm-variable">void</span> <span class="cm-variable">OnParametersSet</span>() { <span class="cm-keyword">if</span> (<span class="cm-variable">AllItems</span>.<span class="cm-property">Count</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) { <span class="cm-comment">// remove the items that exist in SelectedItems</span> <span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">item</span> <span class="cm-keyword">in</span> <span class="cm-variable">SelectedItems</span>) { <span class="cm-keyword">var</span> <span class="cm-def">id</span> <span class="cm-operator">=</span> <span class="cm-variable">item</span>.<span class="cm-property">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">item</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>(); <span class="cm-keyword">var</span> <span class="cm-def">ItemFromAllItems</span> <span class="cm-operator">=</span> (<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span> <span class="cm-variable">where</span> <span class="cm-variable">x</span>.<span class="cm-variable">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">x</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>() <span class="cm-operator">==</span> <span class="cm-variable">id</span> <span class="cm-variable">select</span> <span class="cm-variable">x</span>).<span class="cm-variable">FirstOrDefault</span>(); <span class="cm-keyword">if</span> (<span class="cm-variable">ItemFromAllItems</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) { <span class="cm-variable">AllItems</span>.<span class="cm-property">Remove</span>(<span class="cm-variable">ItemFromAllItems</span>); } } } <span class="cm-keyword">if</span> (<span class="cm-variable">AllItems</span>.<span class="cm-property">Count</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> <span class="cm-variable">AllItems</span>.<span class="cm-property">First</span>(); } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">SelectedItems</span>.<span class="cm-property">Count</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> <span class="cm-variable">SelectedItems</span>.<span class="cm-property">First</span>(); } <span class="cm-variable">UpdateButtonEnabledStates</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">ItemDblClickedFromAllItems</span>() { <span class="cm-variable">AddSelectedItem</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">ItemDblClickedFromSelectedItems</span>() { <span class="cm-variable">RemoveSelectedItem</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">ItemSelectedFromAllItems</span>(<span class="cm-variable">ChangeEventArgs</span> <span class="cm-variable">args</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> (<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span> <span class="cm-variable">where</span> <span class="cm-variable">x</span>.<span class="cm-variable">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">x</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>() <span class="cm-operator">==</span> <span class="cm-variable">args</span>.<span class="cm-property">Value</span>.<span class="cm-property">ToString</span>() <span class="cm-variable">select</span> <span class="cm-variable">x</span>).<span class="cm-variable">FirstOrDefault</span>(); <span class="cm-variable">UpdateButtonEnabledStates</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">UpdateButtonEnabledStates</span>() { <span class="cm-variable">AddSelectedItemButtonDisabled</span> <span class="cm-operator">=</span> <span class="cm-operator">!</span><span class="cm-variable">AllItems</span>.<span class="cm-property">Contains</span>(<span class="cm-variable">SelectedItem</span>); <span class="cm-variable">RemoveSelectedItemButtonDisabled</span> <span class="cm-operator">=</span> <span class="cm-operator">!</span><span class="cm-variable">SelectedItems</span>.<span class="cm-property">Contains</span>(<span class="cm-variable">SelectedItem</span>); } <span class="cm-variable">void</span> <span class="cm-variable">AddAllItems</span>() { <span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">Item</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span>.<span class="cm-variable">ToArray</span>()) { <span class="cm-variable">SelectedItems</span>.<span class="cm-property">Add</span>(<span class="cm-variable">Item</span>); } <span class="cm-keyword">if</span> (<span class="cm-variable">SelectedItems</span>.<span class="cm-property">Count</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> <span class="cm-variable">SelectedItems</span>.<span class="cm-property">First</span>(); } <span class="cm-variable">AllItems</span>.<span class="cm-property">Clear</span>(); <span class="cm-variable">UpdateButtonEnabledStates</span>(); <span class="cm-variable">ComponentUpdated</span>.<span class="cm-property">InvokeAsync</span>(<span class="cm-string">""</span>).<span class="cm-property">Wait</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">RemoveAllItems</span>() { <span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">Item</span> <span class="cm-keyword">in</span> <span class="cm-variable">SelectedItems</span>.<span class="cm-variable">ToArray</span>()) { <span class="cm-variable">AllItems</span>.<span class="cm-property">Add</span>(<span class="cm-variable">Item</span>); } <span class="cm-keyword">if</span> (<span class="cm-variable">AllItems</span>.<span class="cm-property">Count</span> <span class="cm-operator">></span> <span class="cm-number">0</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> <span class="cm-variable">AllItems</span>.<span class="cm-property">First</span>(); } <span class="cm-variable">SelectedItems</span>.<span class="cm-property">Clear</span>(); <span class="cm-variable">UpdateButtonEnabledStates</span>(); <span class="cm-variable">ComponentUpdated</span>.<span class="cm-property">InvokeAsync</span>(<span class="cm-string">""</span>).<span class="cm-property">Wait</span>(); } <span class="cm-variable">void</span> <span class="cm-variable">AddSelectedItem</span>() { <span class="cm-keyword">if</span> ((<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">SelectedItems</span> <span class="cm-variable">where</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">x</span>) <span class="cm-operator">==</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">SelectedItem</span>) <span class="cm-variable">select</span> <span class="cm-variable">x</span>).<span class="cm-variable">FirstOrDefault</span>() <span class="cm-operator">==</span> <span class="cm-atom">null</span>) { <span class="cm-variable">SelectedItems</span>.<span class="cm-property">Add</span>(<span class="cm-variable">SelectedItem</span>); <span class="cm-variable">AllItems</span>.<span class="cm-property">Remove</span>(<span class="cm-variable">SelectedItem</span>); <span class="cm-variable">UpdateButtonEnabledStates</span>(); <span class="cm-variable">ComponentUpdated</span>.<span class="cm-property">InvokeAsync</span>(<span class="cm-string">""</span>).<span class="cm-property">Wait</span>(); } } <span class="cm-variable">void</span> <span class="cm-variable">RemoveSelectedItem</span>() { <span class="cm-keyword">if</span> ((<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span> <span class="cm-variable">where</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">x</span>) <span class="cm-operator">==</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">SelectedItem</span>) <span class="cm-variable">select</span> <span class="cm-variable">x</span>).<span class="cm-variable">FirstOrDefault</span>() <span class="cm-operator">==</span> <span class="cm-atom">null</span>) { <span class="cm-variable">AllItems</span>.<span class="cm-property">Add</span>(<span class="cm-variable">SelectedItem</span>); <span class="cm-variable">SelectedItems</span>.<span class="cm-property">Remove</span>(<span class="cm-variable">SelectedItem</span>); <span class="cm-variable">UpdateButtonEnabledStates</span>(); <span class="cm-variable">ComponentUpdated</span>.<span class="cm-property">InvokeAsync</span>(<span class="cm-string">""</span>).<span class="cm-property">Wait</span>(); } } <span class="cm-variable">void</span> <span class="cm-variable">ItemSelectedFromSelectedItems</span>(<span class="cm-variable">ChangeEventArgs</span> <span class="cm-variable">args</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> (<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">SelectedItems</span> <span class="cm-variable">where</span> <span class="cm-variable">x</span>.<span class="cm-variable">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">x</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>() <span class="cm-operator">==</span> <span class="cm-variable">args</span>.<span class="cm-property">Value</span>.<span class="cm-property">ToString</span>() <span class="cm-variable">select</span> <span class="cm-variable">x</span> ).<span class="cm-variable">FirstOrDefault</span>(); <span class="cm-variable">UpdateButtonEnabledStates</span>(); } }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_129684"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Let’s take a look at the very first line:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_144898"><div class="sqs-block-content"><pre class="source-code">@<span class="cm-variable">typeparam</span> <span class="cm-variable">TItem</span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_146075"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">This is how we can define a data type passed in as parameters. Look at the AllItems parameter:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_146558"><div class="sqs-block-content"><pre class="source-code"> [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">List</span><span class="cm-operator"><</span><span class="cm-variable">TItem</span><span class="cm-operator">></span> <span class="cm-variable">AllItems</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_161578"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">When you create an instance of this component, you pass in a list of whatever you want.</p><p class="" style="white-space:pre-wrap;">The component has to have a way to access the properties we need to use, one for the text that gets displayed, and another for the value (Id) that identifies the object. We expose these property names as parameters:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_162513"><div class="sqs-block-content"><pre class="source-code"> [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">TextPropertyName</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; } [<span class="cm-variable">Parameter</span>] <span class="cm-variable">public</span> <span class="cm-variable">string</span> <span class="cm-variable">ValuePropertyName</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_163694"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Take a look at how we instantiate this component:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_164346"><div class="sqs-block-content"><pre class="source-code"><span class="cm-operator"><</span><span class="cm-variable">ObjectPicker</span> @<span class="cm-variable">ref</span><span class="cm-operator">=</span><span class="cm-string">"InstrumentPicker"</span> <span class="cm-variable">ItemType</span><span class="cm-operator">=</span><span class="cm-string">"Instrument"</span> <span class="cm-variable">ItemTypePlural</span><span class="cm-operator">=</span><span class="cm-string">"Instruments"</span> <span class="cm-variable">AllItems</span><span class="cm-operator">=</span><span class="cm-string">"Instruments"</span> <span class="cm-variable">SelectedItems</span><span class="cm-operator">=</span><span class="cm-string">"SelectedInstruments"</span> <span class="cm-variable">TextPropertyName</span><span class="cm-operator">=</span><span class="cm-string">"Name"</span> <span class="cm-variable">ValuePropertyName</span><span class="cm-operator">=</span><span class="cm-string">"InstrumentId"</span> <span class="cm-variable">ComponentUpdated</span> <span class="cm-operator">=</span> <span class="cm-string">"ComponentUpdated"</span> <span class="cm-operator">/></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_210385"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">ItemType and ItemTypePlural are strings that define what the user is looking at.</p><p class="" style="white-space:pre-wrap;">TextPropertyName is the name of the property in the class that will be displayed</p><p class="" style="white-space:pre-wrap;">ValuePropertyName is the name of the Id property, in this case InstrumentId</p><p class="" style="white-space:pre-wrap;">AllItems is a list of all the items that show up on the left.</p><p class="" style="white-space:pre-wrap;">SelectedItems is a list of all the items on the right</p><p class="" style="white-space:pre-wrap;">In the ObjectPicker itself, we have this field:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_211770"><div class="sqs-block-content"><pre class="source-code"><span class="cm-variable">TItem</span> <span class="cm-variable">SelectedItem</span> { <span class="cm-variable">get</span>; <span class="cm-variable">set</span>; }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_212949"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Whenever an item is selected in either of the <select> boxes, this is the item that was clicked on.</p><p class="" style="white-space:pre-wrap;">Let’s look at the <select> on the left, which shows AllItems:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_213705"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-operator"><</span><span class="cm-variable">select</span> @<span class="cm-variable">ondblclick</span><span class="cm-operator">=</span><span class="cm-string">"ItemDblClickedFromAllItems"</span> @<span class="cm-variable">onchange</span><span class="cm-operator">=</span><span class="cm-string">"ItemSelectedFromAllItems"</span> <span class="cm-variable">size</span><span class="cm-operator">=</span><span class="cm-string">"10"</span> <span class="cm-variable">style</span><span class="cm-operator">=</span><span class="cm-string">"width:100%;"</span><span class="cm-operator">></span> @<span class="cm-variable">foreach</span> (<span class="cm-keyword">var</span> <span class="cm-variable">Item</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span>) { <span class="cm-keyword">if</span> (@<span class="cm-variable">ItemValue</span>(<span class="cm-variable">Item</span>) <span class="cm-operator">==</span> @<span class="cm-variable">ItemValue</span>(<span class="cm-variable">SelectedItem</span>)) { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">selected</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } <span class="cm-keyword">else</span> { <span class="cm-operator"><</span><span class="cm-variable">option</span> <span class="cm-variable">value</span><span class="cm-operator">=</span><span class="cm-string">"@ItemValue(Item)"</span><span class="cm-operator">></span> @<span class="cm-variable">ItemText</span>(<span class="cm-variable">Item</span>) <span class="cm-operator"><</span><span class="cm-string-2">/option></span> } } <span class="cm-operator"><</span><span class="cm-string-2">/select></span></pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_249969"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">Notice we’re getting the display text and value from two methods: ItemValue and ItemText:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_433886"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">private</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemValue</span>(<span class="cm-variable">TItem</span> <span class="cm-variable">Item</span>) { <span class="cm-keyword">return</span> <span class="cm-variable">Item</span>.<span class="cm-property">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">Item</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>(); } <span class="cm-variable">private</span> <span class="cm-variable">string</span> <span class="cm-variable">ItemText</span>(<span class="cm-variable">TItem</span> <span class="cm-variable">Item</span>) { <span class="cm-keyword">return</span> <span class="cm-variable">Item</span>.<span class="cm-property">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">TextPropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">Item</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>(); }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_435038"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">We use a bit of reflection to get the values of these properties. We also use the same technique in a LINQ query to match the selected item based on the value of the value property. This method is called when an item is selected:</p> </div> </div></div><div class="sqs-block code-block sqs-block-code" data-block-type="23" id="block-yui_3_17_2_1_1571245501717_454694"><div class="sqs-block-content"><pre class="source-code"> <span class="cm-variable">void</span> <span class="cm-variable">ItemSelectedFromAllItems</span>(<span class="cm-variable">ChangeEventArgs</span> <span class="cm-variable">args</span>) { <span class="cm-variable">SelectedItem</span> <span class="cm-operator">=</span> (<span class="cm-variable">from</span> <span class="cm-variable">x</span> <span class="cm-keyword">in</span> <span class="cm-variable">AllItems</span> <span class="cm-variable">where</span> <span class="cm-variable">x</span>.<span class="cm-variable">GetType</span>() .<span class="cm-property">GetProperty</span>(<span class="cm-variable">ValuePropertyName</span>) .<span class="cm-property">GetValue</span>(<span class="cm-variable">x</span>, <span class="cm-atom">null</span>) .<span class="cm-property">ToString</span>() <span class="cm-operator">==</span> <span class="cm-variable">args</span>.<span class="cm-property">Value</span>.<span class="cm-property">ToString</span>() <span class="cm-variable">select</span> <span class="cm-variable">x</span>).<span class="cm-variable">FirstOrDefault</span>(); <span class="cm-variable">UpdateButtonEnabledStates</span>(); }</pre></div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1571245501717_475082"><div class="sqs-block-content"> <div class="sqs-html-content"> <p class="" style="white-space:pre-wrap;">The rest of it is just enabling and disabling buttons, and moving items from one side to the other. </p><p class="" style="white-space:pre-wrap;">The host app can use the SelectedItems list to update the actual objects, entities, or what have you.</p><p class="" style="white-space:pre-wrap;">Want more Blazor? How about a one-day workshop online? Details at <a href="http://blazor.appvnext.com" target="_blank">http://blazor.appvnext.com</a></p><p class="" style="white-space:pre-wrap;">Carl</p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2019/10/16/building-a-reusable-generic-template-component-for-server-side-blazor" class="sqs-comment-link sqs-disqus-comment-link" data-id="5da74dca30ee601884745644"></a> <span class="sqs-simple-like" data-item-id="5da74dca30ee601884745644" data-like-count="2"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/5da74dca30ee601884745644/1574775007195/" data-record-type="1" data-full-url="/blog/2019/10/16/building-a-reusable-generic-template-component-for-server-side-blazor" data-title="Building a reusable generic template component for server-side Blazor"></span> </div> </footer> </article> <article class="entry h-entry hentry author-carl-franklin post-type-text article-index-20" id="article-59f468a3e4966b0784b8dd97" data-item-id="59f468a3e4966b0784b8dd97"> <header class="entry-header"> <div class="meta-above-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2017-10-28" pubdate><a href="/blog/2017/10/28/xamarin-forms-gets-mac-desktop-and-more" class="entry-dateline-link">October 28, 2017</a></time> <time class="dt-updated updated" datetime="2017-10-28"></time> </div> </div> <h1 data-content-field="title" class="entry-title p-name"> <a href="/blog/2017/10/28/xamarin-forms-gets-mac-desktop-and-more" class="u-url" rel="bookmark">Xamarin Forms gets Mac Desktop and more!</a> </h1> <div class="meta-below-title"> <div class="entry-byline"> <span class="entry-author"><a href="/blog?author=56057d84e4b0ba7911a46347" class="p-author author entry-byline-link" rel="author">Carl Franklin</a></span> </div> <div class="entry-dateline"> <time class="dt-published published entry-date" datetime="2017-10-28" pubdate><a href="/blog/2017/10/28/xamarin-forms-gets-mac-desktop-and-more" class="entry-dateline-link">October 28, 2017</a></time> <time class="dt-updated updated" datetime="2017-10-28"></time> </div> </div> </header> <div class="entry-content"> <div class="e-content"><div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Post Body" data-type="item" data-updated-on="1509190899175" id="item-59f468a3e4966b0784b8dd97"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-23e52a145f0617d53741"><div class="sqs-block-content"> <div class="sqs-html-content"> <p>I was just watching the latest <a target="_blank" href="https://channel9.msdn.com/Shows/XamarinShow/Episode-32-Whats-new-in-XamarinForms-24-with-David-Ortinau">Xamarin Show</a> with James Montemagno and was pleasantly surprised to see that <a target="_blank" href="https://developer.xamarin.com/releases/xamarin-forms/xamarin-forms-2.4/2.4.0-sr2/">Xamarin.Forms 2.4.0 Service Release 2</a> (September, 2017) added support for Mac Desktop apps. Of course, you have to use Visual Studio for the Mac, but that's completely understandable. In this episode, David Ortinau showed the gallery sample app running as a Mac desktop app. Very very cool.</p> </div> </div></div><div class="sqs-block image-block sqs-block-image sqs-col-6 span-6 float float-left" data-block-type="5" id="block-yui_3_17_2_1_1509189775028_17459"><div class="sqs-block-content"> <div class=" image-block-outer-wrapper layout-caption-below design-layout-inline " data-test="image-block-inline-outer-wrapper" > <figure class=" sqs-block-image-figure intrinsic " style="max-width:1050px;" > <div class="image-block-wrapper" data-animation-role="image" data-animation-override > <div class="sqs-image-shape-container-element has-aspect-ratio " style=" position: relative; padding-bottom:100.3809585571289%; overflow: hidden;-webkit-mask-image: -webkit-radial-gradient(white, black); " > <img data-stretch="false" data-src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png" data-image="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png" data-image-dimensions="1050x1054" data-image-focal-point="0.5,0.5" alt="formsMacOs.png" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png" width="1050" height="1054" alt="" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" style="display:block;object-fit: cover; width: 100%; height: 100%; object-position: 50% 50%" onload="this.classList.add("loaded")" srcset="https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/56056b47e4b043d49b646e7e/1509190128767-BH63NDRSOFYNSECXIPBS/formsMacOs.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs"> </div> </div> </figure> </div> </div></div><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-yui_3_17_2_1_1509189775028_21135"><div class="sqs-block-content"> <div class="sqs-html-content"> <p>They also made performance improvements by tightening up the number of UI layers required for basic controls. These improvements are especially noticeable with lists.</p><p>On top of that, they announced support for <a target="_blank" href="https://blog.xamarin.com/xamarin-forms-stable-comes-to-net-standard-2-0/">.NET Standard 2.0</a> (and therefore lower)</p><p>These features are new. Think of them as alpha. </p> </div> </div></div></div></div></div></div> </div> <footer class="entry-footer clear"> <div class="entry-actions"> <a href="/blog/2017/10/28/xamarin-forms-gets-mac-desktop-and-more" class="sqs-comment-link sqs-disqus-comment-link" data-id="59f468a3e4966b0784b8dd97"></a> <span class="sqs-simple-like" data-item-id="59f468a3e4966b0784b8dd97" data-like-count="0"> <span class="like-icon"></span> <span class="like-count"></span> </span> <span class="squarespace-social-buttons inline-style" data-system-data-id="" data-asset-url="https://static1.squarespace.com/static/56056b47e4b043d49b646e7e/56056be1e4b0403251121b20/59f468a3e4966b0784b8dd97/1509190900447/" data-record-type="1" data-full-url="/blog/2017/10/28/xamarin-forms-gets-mac-desktop-and-more" data-title="Xamarin Forms gets Mac Desktop and more!"></span> </div> </footer> </article> <nav class="pagination clear" data-paginate="scroll"> <div class="newer"></div><!-- comment the linebreak between these two elements because science --><div class="older"><a href="/blog?offset=1509190899170" rel="next"><span class="next-label">Older Posts</span></a></div> </nav> </div> </div><!-- comment the linebreak between these two elements because science --><aside id="rightSidebar" role="complementary"> <div class="sqs-layout sqs-grid-12 columns-12 open-block-field empty" data-layout-label="Blog Sidebar Content" data-type="block-field" id="blogBlocks-56056be1e4b0403251121b20"><div class="row sqs-row"><div class="col sqs-col-12 span-12"></div></div></div> </aside> </main> <div id="preFooter"> <div class="pre-footer-inner"> <div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Pre-Footer Content" data-type="block-field" data-updated-on="1406661050940" id="preFooterBlocks"><div class="row sqs-row"><div class="col sqs-col-12 span-12"></div></div></div> </div> </div> <footer id="footer" role="contentinfo"> <div class="footer-inner"> <div class="nav-wrapper back-to-top-nav"><nav><div class="back-to-top"><a href="#header">Back to Top</a></div></nav></div> <div id="siteInfo"><span class="site-address">App vNext</span><a href="mailto:info@appvnext.com" class="site-email"><span>info@appvnext.com</span></a></div> <div class="sqs-layout sqs-grid-12 columns-12" data-layout-label="Footer Content" data-type="block-field" data-updated-on="1580661679465" id="footerBlocks"><div class="row sqs-row"><div class="col sqs-col-12 span-12"><div class="sqs-block html-block sqs-block-html" data-block-type="2" id="block-713fe491a2c303b33937"><div class="sqs-block-content"> <div class="sqs-html-content"> <p style="text-align:center;white-space:pre-wrap;" class="">© 2020 APP VNEXT</p> </div> </div></div></div></div></div> </div> </footer> </div> <script type="text/javascript" src="https://static1.squarespace.com/static/ta/56056b47e4b043d49b646e7e/6/scripts/combo/?site.js&helpers.js&sticky.js"></script> <style type="text/css"> pre.hljs { font-size: small; margin: 0; } div.hljs { border: 1px solid #eee8d5; } .source-code { line-height: 1.4em; } </style> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"></script> <script> Y.all('pre.source-code').each(function () { hljs.highlightBlock(this.getDOMNode()); this.ancestor('div').addClass('hljs'); }); </script><script data-sqs-type="imageloader-bootstrapper">if(window.ImageLoader) window.ImageLoader.bootstrap({}, document);</script><script>Squarespace.afterBodyLoad(Y);</script> </body> </html>